如何防止默认处理触摸事件?

新手上路,请多包涵

我正在尝试做类似于 嵌入式 Google 地图 所做的事情。我的组件应该忽略单点触摸(允许用户滚动页面)并在自身外部捏合(允许用户缩放页面),但应该对双点触摸做出反应(允许用户在组件内部导航)并且在这种情况下不允许任何默认操作。

如何防止触摸事件的默认处理,但仅限于用户用两根手指与我的组件交互的情况?

我试过的:

  1. 我尝试捕获 onTouchStartonTouchMoveonTouchEnd 。事实证明,在 FF Android 上,在组件上捏合时触发的第一个事件是 onTouchStart 一次触摸,然后 onTouchStart 两次触摸,然后 onTouchMove a-2c .但是调用 event.preventDefault()event.stopPropagation()onTouchMove 处理程序不会(总是)停止页面缩放/滚动在第一次调用 onTouchStart 时防止事件升级确实 有帮助- 不幸的是,当时我还不知道它是否会是多点触控,所以我不能使用它。

  2. 第二种方法是在 touch-action: none 上设置 document.body 。这适用于 Chrome Android,但如果我在所有元素(我的组件除外)上设置它,我只能让它与 Firefox Android 一起使用。因此,虽然这是可行的,但它似乎可能会产生不必要的副作用和性能问题。 编辑: 进一步的测试表明,只有在触摸开始之前设置了 CSS,这才适用于 Chrome。换句话说,如果我在检测到 2 个手指时注入 CSS 样式,那么 touch-action 将被忽略。所以这在 Chrome 上没有用。

  3. 我还尝试在组件挂载上添加一个新的事件监听器:

    document.body.addEventListener("touchmove", ev => {
     ev.preventDefault();
     ev.stopImmediatePropagation();
   }, true);

(和 `touchstart` 一样)。这样做在 Firefox Android 中有效,但在 Chrome Android 中不起作用。

我的想法不多了。是否有一种可靠的跨浏览器方式来实现谷歌显然所做的事情,或者他们是否在每个浏览器上使用了多个 hack 和大量测试来使其工作?如果有人指出我的方法中的错误或提出新方法,我将不胜感激。

原文由 johndodo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 481
2 个回答

TL;DR:我在注册事件处理程序时缺少 { passive: false }

我与 preventDefault() 与 Chrome 的问题是由于他们的 滚动“干预” (阅读:打破网络 IE 风格)。简而言之,因为可以更快地处理不调用 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,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果不是这种情况,它可能性能更高,并且似乎 在所有适用平台上都受支持(Mac 上的 Safari 不支持,但在 iOS 上支持)。 这篇文章 说得最好:

对于您的情况,您可能希望将文本区域(或其他)标记为“触摸操作:无”以禁用滚动/缩放而不禁用所有其他行为。

应该在组件上设置 CSS 属性,而不是像我那样在 document 上设置:

 <div style="touch-action: none;">
  ... my component ...
</div>

在我的情况下,我仍然需要使用 passive 事件处理程序,但很高兴知道这些选项……希望它能帮助别人。

原文由 johndodo 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是我的想法:

I used one div with opacity: 0 to cover the map with z-index > map z-index

我会在 covered div 中检测到。如果我检测到 2 根手指 在此 covered div 中触摸,我将 display: none this div 允许用户在此地图中使用 2 根手指。

否则,在 document touchEnd 我将恢复这个 covered div 使用 display: block 以确保我们可以滚动。

原文由 Tan Duong 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题