javascript 如何防止触摸事件的默认处理?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/49541173/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent default handling of touch events?
提问by johndodo
I am trying to do something similar to what embedded Google mapsdo. My component should ignore single touch (allowing user to scroll page) and pinch outside of itself (allowing user to zoom page), but should react to double touch (allowing user to navigate inside the component) and disallow any default action in this case.
我正在尝试做一些类似于嵌入式谷歌地图所做的事情。我的组件应该忽略单点触摸(允许用户滚动页面)并在其外部捏合(允许用户缩放页面),但应该对双触摸做出反应(允许用户在组件内部导航)并在这种情况下禁止任何默认操作。
How do I prevent default handling of touch events, but only in the case when user is interacting with my component with two fingers?
如何防止触摸事件的默认处理,但仅在用户用两根手指与我的组件交互的情况下?
What I have tried:
我尝试过的:
I tried capturing
onTouchStart,onTouchMoveandonTouchEnd. It turns out that on FF Android the first event that fires when doing pinch on component isonTouchStartwith a single touch, thenonTouchStartwith two touches, thenonTouchMove. But callingevent.preventDefault()orevent.stopPropagation()inonTouchMovehandler doesn't (always) stop page zoom/scroll. Preventing event escalation in the first call toonTouchStartdoes help- unfortunately at that time I don't know yet if it's going to be multitouch or not, so I can't use this.Second approach was setting
touch-action: noneondocument.body. This works with Chrome Android, but I could only make it work with Firefox Android if I set this on all elements (except for my component). So while this is doable, it seems like it could have unwanted side effects and performance issues. EDIT:Further testing revealed that this works for Chrome only if the CSS is set before the touch has started. In other words, if I inject CSS styles when I detect 2 fingers thentouch-actionis ignored. So this is not useful on Chrome.I have also tried adding a new event listener on component mount:
document.body.addEventListener("touchmove", ev => { ev.preventDefault(); ev.stopImmediatePropagation(); }, true);(and the same for
touchstart). Doing so works in Firefox Android, but does nothing on Chrome Android.
我尝试捕获
onTouchStart,onTouchMove和onTouchEnd. 事实证明,在 FF Android 上,在组件上捏合时触发的第一个事件是onTouchStart单次触摸,然后onTouchStart是两次触摸,然后是onTouchMove. 但是调用event.preventDefault()或event.stopPropagation()inonTouchMove处理程序不会(总是)停止页面缩放/滚动。在第一次调用中防止事件升级onTouchStart确实有帮助- 不幸的是,当时我还不知道它是否会是多点触控,所以我不能使用它。第二条本办法中设置
touch-action: none的document.body。这适用于 Chrome Android,但如果我在所有元素(除了我的组件)上设置它,我只能使它适用于 Firefox Android。因此,虽然这是可行的,但它似乎可能会产生不需要的副作用和性能问题。编辑:进一步的测试表明,只有在触摸开始之前设置了 CSS,这才适用于 Chrome。换句话说,如果我在检测到 2 个手指时注入 CSS 样式,则将touch-action被忽略。所以这在 Chrome 上没有用。我还尝试在组件安装上添加一个新的事件侦听器:
document.body.addEventListener("touchmove", ev => { ev.preventDefault(); ev.stopImmediatePropagation(); }, true);(和相同
touchstart)。这样做在 Firefox Android 中有效,但在 Chrome Android 上不起作用。
I am running out of ideas. Is there a reliable cross-browser way to achieve what Google apparently did, or did they use multiple hacks and lots of testing on every browser to make it work? I would appreciate if someone pointed out an error in my approach(es) or propose a new way.
我的想法不多了。是否有一种可靠的跨浏览器方式来实现谷歌显然所做的事情,或者他们是否在每个浏览器上使用了多次黑客和大量测试来使其工作?如果有人指出我的方法中的错误或提出新方法,我将不胜感激。
回答by johndodo
TL;DR: I was missing { passive: false }when registering event handlers.
TL;DR:我{ passive: false }在注册事件处理程序时丢失了。
The issue I had with preventDefault()with Chrome was due to their scrolling "intervention"(read: breaking the web IE-style). In short, because the handlers that don't call preventDefault()can be handled faster, a new option was added to addEventListenernamed passive. If set to truethen event handler promises not to call preventDefault(if it does, the call will be ignored). Chrome however decided to go a step further and make {passive: true}default (since version 56).
我在preventDefault()Chrome 上遇到的问题是由于它们的滚动“干预”(阅读:破坏网络 IE 风格)。简而言之,因为preventDefault()可以更快地处理不调用的处理程序,所以在addEventListenernamed中添加了一个新选项passive。如果设置为truethen 事件处理程序承诺不调用preventDefault(如果调用,调用将被忽略)。然而,Chrome 决定更进一步并{passive: true}设置默认值(自版本 56 起)。
Solution is calling the event listener with passiveexplicitly set to false:
解决方案是调用passive显式设置为的事件侦听器false:
window.addEventListener('touchmove', ev => {
if (weShouldStopDefaultScrollAndZoom) {
ev.preventDefault();
ev.stopImmediatePropagation();
};
}, { passive: false });
Note that this negatively impacts performance.
请注意,这会对性能产生负面影响。
As a side note, it seems I misunderstood touch-actionCSS, however I still can't use it because it needs to be set before touch sequence starts. But if this is not the case, it is probably more performant and seems to be supported on all applicable platforms(Safari on Mac does not support it, but on iOS it does). This postsays it best:
作为旁注,我似乎误解了touch-actionCSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前进行设置。但如果不是这种情况,它的性能可能更高,并且似乎在所有适用平台上都受支持(Mac 上的 Safari 不支持,但在 iOS 上支持)。这篇文章说得最好:
For your case you probably want to mark your text area (or whatever) 'touch-action: none' to disable scrolling/zooming without disabling all the other behaviors.
对于您的情况,您可能希望将文本区域(或其他任何内容)标记为“touch-action: none”以禁用滚动/缩放而不禁用所有其他行为。
The CSS property should be set on the component and not on documentas I did it:
CSS 属性应该设置在组件上,而不是document像我那样设置:
<div style="touch-action: none;">
... my component ...
</div>
In my case I will still need to use passiveevent handlers, but it's good to know the options... Hope it helps someone.
在我的情况下,我仍然需要使用passive事件处理程序,但很高兴知道这些选项......希望它对某人有所帮助。
回答by Fig
Try using an if statement to see if there is more than one touch:
尝试使用 if 语句来查看是否有多个触摸:
document.body.addEventListener("touchmove", ev => {
if (ev.touches.length > 1) {
ev.preventDefault();
ev.stopImmediatePropagation();
}
}, true);
回答by Tan Duong
This is my idea:
这是我的想法:
I used one divwith opacity: 0to cover the mapwith z-index> map z-index
我用了一个divwithopacity: 0来覆盖mapwith z-index> mapz-index
And I will detect in the covered div. If I detect 2 fingerstouched in this covered div, I will display: nonethis divto allow user can use 2 finger in this map.
我会在covered div. 如果我发现2个手指在这种感动covered div,我会display: none这样div,让用户可以使用手指2在此地图。
Otherwise, in document touchEndI will recover this covered divusing display: blockto make sure we can scroll.
否则,document touchEnd我将在恢复covered div使用display: block,以确保我们可以滚动。
回答by Leonardo Pineda
In my case I have solved it with
就我而言,我已经解决了
@HostListener('touchmove', ['$event'])
public onTouch(event: any): void {
event.stopPropagation();
console.log('onTouch', event);
}

