如何防止默认处理触摸事件?
How to prevent default handling of touch events?
我正在尝试做类似于 embedded Google maps 所做的事情。我的组件应该忽略单点触摸(允许用户滚动页面)并在自身外部捏合(允许用户缩放页面),但应该对双点触摸做出反应(允许用户在组件内部导航)并且在这种情况下不允许任何默认操作。
如何防止触摸事件的默认处理,但仅限于用户用两根手指与我的组件交互的情况?
我尝试过的:
我尝试捕获 onTouchStart
、onTouchMove
和 onTouchEnd
。事实证明,在 FF Android 上,在组件上捏合时触发的第一个事件是单击 onTouchStart
,然后是两次触摸 onTouchStart
,然后是 onTouchMove
。但是在 onTouchMove
处理程序中调用 event.preventDefault()
或 event.stopPropagation()
不会(总是)停止页面 zoom/scroll。在第一次调用 onTouchStart
时防止事件升级确实有帮助 - 不幸的是,那时我还不知道它是否会是多点触控,所以我不能使用这个。
第二种方法是在 document.body
上设置 touch-action: none
。这适用于 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.
中不起作用
我运行没主意了。是否有一种可靠的跨浏览器方式来实现 Google 显然所做的,或者他们是否在每个浏览器上使用了多个 hack 和大量测试来使其工作?如果有人指出我的方法中的错误或提出新方法,我将不胜感激。
尝试使用 if 语句查看是否有多个触摸:
document.body.addEventListener("touchmove", ev => {
if (ev.touches.length > 1) {
ev.preventDefault();
ev.stopImmediatePropagation();
}
}, true);
TL;DR:我在注册事件处理程序时缺少 { passive: false }
。
我对 preventDefault()
和 Chrome 的问题是由于他们的 scrolling "intervention"(阅读:破坏网络 IE-style)。简而言之,由于可以更快地处理不调用 preventDefault()
的处理程序,因此向 addEventListener
添加了一个名为 passive
的新选项。如果设置为 true
,则事件处理程序承诺不调用 preventDefault
(如果调用,调用将被忽略)。 Chrome 然而决定更进一步,使 {passive: true}
默认(从版本 56 开始)。
解决方案是调用事件侦听器 passive
显式设置为 false
:
window.addEventListener('touchmove', ev => {
if (weShouldStopDefaultScrollAndZoom) {
ev.preventDefault();
ev.stopImmediatePropagation();
};
}, { passive: false });
请注意,这会对性能产生负面影响。
作为旁注,我似乎误解了touch-action
CSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果不是这种情况,它可能性能更高,似乎 supported on all applicable platforms (Safari on Mac does not support it, but on iOS it does). This post 说得最好:
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.
应该在组件上设置 CSS 属性,而不是像我那样在 document
上设置:
<div style="touch-action: none;">
... my component ...
</div>
在我的情况下,我仍然需要使用 passive
事件处理程序,但了解选项是很好的...希望它能帮助别人。
这是我的想法:
我用 div
和 opacity: 0
来覆盖 map
和 z-index
> 地图 z-index
而我会在covered div
中进行检测。如果我在这个 covered div
中检测到 2 fingers 被触摸,我将 display: none
这个 div
允许用户可以在这个地图中使用 2 个手指。
否则,在 document touchEnd
我将使用 display: block
恢复此 covered div
以确保我们可以滚动。
在我的例子中,我已经用
解决了它
@HostListener('touchmove', ['$event'])
public onTouch(event: any): void {
event.stopPropagation();
console.log('onTouch', event);
}
我正在尝试做类似于 embedded Google maps 所做的事情。我的组件应该忽略单点触摸(允许用户滚动页面)并在自身外部捏合(允许用户缩放页面),但应该对双点触摸做出反应(允许用户在组件内部导航)并且在这种情况下不允许任何默认操作。
如何防止触摸事件的默认处理,但仅限于用户用两根手指与我的组件交互的情况?
我尝试过的:
我尝试捕获
onTouchStart
、onTouchMove
和onTouchEnd
。事实证明,在 FF Android 上,在组件上捏合时触发的第一个事件是单击onTouchStart
,然后是两次触摸onTouchStart
,然后是onTouchMove
。但是在onTouchMove
处理程序中调用event.preventDefault()
或event.stopPropagation()
不会(总是)停止页面 zoom/scroll。在第一次调用onTouchStart
时防止事件升级确实有帮助 - 不幸的是,那时我还不知道它是否会是多点触控,所以我不能使用这个。第二种方法是在
document.body
上设置touch-action: none
。这适用于 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. 中不起作用
我运行没主意了。是否有一种可靠的跨浏览器方式来实现 Google 显然所做的,或者他们是否在每个浏览器上使用了多个 hack 和大量测试来使其工作?如果有人指出我的方法中的错误或提出新方法,我将不胜感激。
尝试使用 if 语句查看是否有多个触摸:
document.body.addEventListener("touchmove", ev => {
if (ev.touches.length > 1) {
ev.preventDefault();
ev.stopImmediatePropagation();
}
}, true);
TL;DR:我在注册事件处理程序时缺少 { passive: false }
。
我对 preventDefault()
和 Chrome 的问题是由于他们的 scrolling "intervention"(阅读:破坏网络 IE-style)。简而言之,由于可以更快地处理不调用 preventDefault()
的处理程序,因此向 addEventListener
添加了一个名为 passive
的新选项。如果设置为 true
,则事件处理程序承诺不调用 preventDefault
(如果调用,调用将被忽略)。 Chrome 然而决定更进一步,使 {passive: true}
默认(从版本 56 开始)。
解决方案是调用事件侦听器 passive
显式设置为 false
:
window.addEventListener('touchmove', ev => {
if (weShouldStopDefaultScrollAndZoom) {
ev.preventDefault();
ev.stopImmediatePropagation();
};
}, { passive: false });
请注意,这会对性能产生负面影响。
作为旁注,我似乎误解了touch-action
CSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果不是这种情况,它可能性能更高,似乎 supported on all applicable platforms (Safari on Mac does not support it, but on iOS it does). This post 说得最好:
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.
应该在组件上设置 CSS 属性,而不是像我那样在 document
上设置:
<div style="touch-action: none;">
... my component ...
</div>
在我的情况下,我仍然需要使用 passive
事件处理程序,但了解选项是很好的...希望它能帮助别人。
这是我的想法:
我用 div
和 opacity: 0
来覆盖 map
和 z-index
> 地图 z-index
而我会在covered div
中进行检测。如果我在这个 covered div
中检测到 2 fingers 被触摸,我将 display: none
这个 div
允许用户可以在这个地图中使用 2 个手指。
否则,在 document touchEnd
我将使用 display: block
恢复此 covered div
以确保我们可以滚动。
在我的例子中,我已经用
解决了它@HostListener('touchmove', ['$event'])
public onTouch(event: any): void {
event.stopPropagation();
console.log('onTouch', event);
}