如何防止触摸事件的默认处理? [英] How to prevent default handling of touch events?

查看:107
本文介绍了如何防止触摸事件的默认处理?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to do something similar to what embedded Google maps do. 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?

我尝试过:


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

  1. I tried capturing onTouchStart, onTouchMove and onTouchEnd. It turns out that on FF Android the first event that fires when doing pinch on component is onTouchStart with a single touch, then onTouchStart with two touches, then onTouchMove. But calling event.preventDefault() or event.stopPropagation() in onTouchMove handler doesn't (always) stop page zoom/scroll. Preventing event escalation in the first call to onTouchStart does 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.

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

Second approach was setting touch-action: none on document.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. 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 then touch-action is 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);

touchstart 也是如此)。这样做适用于Firefox Android,但在Chrome Android上无效。

(and the same for touchstart). Doing so works in Firefox Android, but does nothing on Chrome Android.

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

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.

推荐答案

TL; DR:注册事件处理程序时,我错过了 {passive:false}

TL;DR: I was missing { passive: false } when registering event handlers.

我遇到的问题 preventDefault() Chrome使用了滚动干预(阅读:打破网络IE风格)。简而言之,因为可以更快地处理不调用 preventDefault()的处理程序,所以在 addEventListener 命名被动。如果设置为 true ,则事件处理程序承诺不会调用 preventDefault (如果是,则会忽略该调用)。然而,Chrome决定更进一步,使 {passive:true} 默认(从版本56开始)。

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 addEventListener named passive. If set to true then 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).

解决方案是将 passive 显式设置为 false 调用事件监听器:

Solution is calling the event listener with passive explicitly set to false:

window.addEventListener('touchmove', ev => {
  if (weShouldStopDefaultScrollAndZoom) {
    ev.preventDefault();
    ev.stopImmediatePropagation();
  };
}, { passive: false });

请注意,这会对业绩产生负面影响。

Note that this negatively impacts performance.

作为旁注,似乎我误解了 touch-action CSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果情况并非如此,它可能更具性能,并且似乎在所有适用平台上都支持 (Mac上的Safari不支持它,但在iOS上它支持)。 这篇文章说得最好:

As a side note, it seems I misunderstood touch-action CSS, 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 post says it best:


对于您的情况,您可能希望标记您的文本区域(或其他)
'touch-action:none'以禁用滚动/缩放而不禁用
所有其他行为。

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属性,而不是文件就像我做的那样:

The CSS property should be set on the component and not on document as I did it:

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

在我的情况下,我仍然需要使用被动事件处理程序,但知道选项很好...希望它可以帮助某人。

In my case I will still need to use passive event handlers, but it's good to know the options... Hope it helps someone.

这篇关于如何防止触摸事件的默认处理?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆