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

时间:2018-03-28 17:55:37

标签: javascript reactjs cross-browser

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

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

我尝试过:

  1. 我尝试抓取onTouchStartonTouchMoveonTouchEnd。事实证明,在FF Android上,第一次触发组件时触发的事件只有onTouchStart触摸,然后onTouchStart触摸两次,然后onTouchMove。但是,在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上无效。

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

4 个答案:

答案 0 :(得分:13)

TL; DR:注册事件处理程序时我遗失了{ passive: false }

我与Chrome preventDefault()的问题归因于他们scrolling "intervention"(阅读:打破网络IE风格)。简而言之,因为可以更快地处理不打电话给preventDefault()的处理程序,所以在名为addEventListener的{​​{1}}中添加了一个新选项。如果设置为passive,则事件处理程序承诺不调用true(如果是,则调用将被忽略)。然而,Chrome决定更进一步,默认preventDefault(从版本56开始)。

解决方案是调用事件监听器,{passive: true}显式设置为passive

false

请注意,这会对效果产生负面影响。

作为旁注,似乎我误解了window.addEventListener('touchmove', ev => { if (weShouldStopDefaultScrollAndZoom) { ev.preventDefault(); ev.stopImmediatePropagation(); }; }, { passive: false }); CSS,但是我仍然无法使用它,因为它需要在触摸序列开始之前设置。但如果情况并非如此,它可能更具性能,似乎是supported on all applicable platforms(Mac上的Safari不支持它,但在iOS上它支持它)。 This post说得最好:

  

对于您的情况,您可能想要标记您的文本区域(或其他)   '触摸动作:无'禁用滚动/缩放而不禁用   所有其他行为。

CSS属性应该在组件上设置,而不是touch-action,就像我做的那样:

document

在我的情况下,我仍然需要使用<div style="touch-action: none;"> ... my component ... </div> 事件处理程序,但了解选项很有用......希望它对某人有所帮助。

答案 1 :(得分:0)

尝试使用if语句查看是否有多个触摸:

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

答案 2 :(得分:0)

这是我的想法:

我使用div一个opacity: 0map覆盖z-index&gt;地图z-index

我会在covered div中发现。如果我在此covered div中检测到2 fingers,我将display: nonediv以允许用户在此地图中使用2个手指。

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

答案 3 :(得分:0)

就我而言,我已经用

解决了
@HostListener('touchmove', ['$event'])
    public onTouch(event: any): void {
    event.stopPropagation();
    console.log('onTouch', event);
}