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

IT技术 javascript reactjs cross-browser
2021-03-28 01:35:52

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

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

我尝试过的:

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

  2. 第二条本办法中设置touch-action: nonedocument.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个回答

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

我在preventDefault()Chrome 上遇到的问题是由于它们的滚动“干预”(阅读:破坏网络 IE 风格)。简而言之,因为preventDefault()可以更快地处理不调用的处理程序,所以在addEventListenernamed中添加了一个新选项passive如果设置为truethen 事件处理程序Promise不调用preventDefault(如果调用,调用将被忽略)。然而,Chrome 决定更进一步并{passive: true}设置默认值(自版本 56 起)。

解决方案是调用passive显式设置为的事件侦听器false

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

请注意,这会对性能产生负面影响。

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

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

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

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

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

只需要其中之一。CSStouch-action选项性能更高(更好),但只有在您提前(在touchstart事件之前设置时才有效在我的情况下,这不是一个选项,因为我需要根据手指的数量不同的行为,所以我不得不等待 touchstart。
2021-05-25 01:35:52
我真的很想从中得到一个更简洁的答案。现在,我实现了一个额外的touchmove事件并向组件添加了 css。两者都是必要的还是我们可以不用组件上的 css?
2021-06-06 01:35:52

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

document.body.addEventListener("touchmove", ev => {
  if (ev.touches.length > 1) {
    ev.preventDefault();
    ev.stopImmediatePropagation();
  }
}, true);
谢谢!我已经尝试过了(请参阅我的问题中的第 3 点)。但是,这并不能阻止在 Android 版 Chrome 上滚动/缩放。请注意,我无法阻止默认操作(在检测到 2 个手指之后),无法确定是否需要阻止。
2021-06-08 01:35:52

这是我的想法:

我用了一个divwithopacity: 0来覆盖mapwith z-index> mapz-index

我会在covered div. 如果我发现2个手指在这种感动covered div,我会display: none这样div,让用户可以使用手指2在此地图。

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

就我而言,我已经解决了

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