H5 touch穿透问题(touch影响click)

45 阅读1分钟

背景

测试同学提了一个bug,列表页点击顶部订单item右下角的充电状态(ex:待结算),跳转到账单推送页后,会同时触发联系客服功能,唤起呼叫面板。

分析

首先订单item的点击事件是通过touch模拟click实现(同时记录用户的touchmove和touchend事件,在touchend触发时,如果用户手指垂直方向没有改动即视为click),考虑到订单item的订单状态“充电中”和账单上传页的“XX客服”是同一个位置,因此首先考虑到的就是订单item上的touch事件穿透到了新的页面触发了“XX客服”上绑定的click事件

实验

<body>
  <div id="box" style="width: 200px; height: 100px; background-color: red">
  </div>
  <script type="text/javascript">
    const box = document.getElementById('box');
    box.addEventListener('click', () => {
      console.log('--------->click');
    });
    box.addEventListener('touchstart', () => {
      console.log('--------->touchstart')
    })
    box.addEventListener('touchmove', () => {
      console.log('--------->touchmove')
    })
    box.addEventListener('touchend', () => {
      console.log('--------->touchend')
    })
  </script>
</body>
  • 当用户点击时,触发顺序 touchstart -> touchend -> click
  • 当用户滑动时,触发顺序 touchstart -> touchmove -> touchend

结论:

当touchend事件触发时,页面会发生跳转,由于click事件是在touchend事件之后触发,此时原来event的target变成了新页面同一位置的元素,而该元素恰好绑定了click事件,就会发生类似于“点击穿透”的现象。

解决方法:

使用preventDefault

第一种解决方案是使用事件对象中的 preventDefault 方法,对于该方法MDN上的解释是

The Event interface's preventDefault() method tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be.

使用preventDefault可以阻止元素默认事件的发生。但是可以观察到的是,通过使用preventDefault可以同样可以阻止后续click事件的发生。

<body>
  <div id="box" style="width: 200px; height: 100px; background-color: red">
  </div>
  <script type="text/javascript">
    const box = document.getElementById('box');
    box.addEventListener('click', () => {
      console.log('--------->click');
    });
    box.addEventListener('touchend', () => {
			event.preventDefault();
      console.log('--------->touchend')
    })
  </script>
</body>

当用户点击时,触发顺序 touchstart -> touchend

参考:

如何解决 touchstart 事件与 click 事件的冲突:segmentfault.com/a/119000001…