探讨移动端点击穿透的问题.

我们都知道,在有了移动端之后,就多了以 touch 开头的事件类型.

  • touchstart
  • touchmove
  • touchend
  • touchcancel

为什么浏览器端在本身有了 click 事件时候,还非要针对移动端设置新的 touch 事件呢? click 也可以相应用户点击啊?你管是用户在移动端是用手指点的,还是在PC端是用鼠标点的呢?

300ms 延迟是怎么回事?

我相信很多人都知道 300ms,这个在移动端里经常提到的数字,但它到底是什么含义的?

当在移动端绑定click事件时 .

<button id="btn">在移动端相应click事件的按钮?</button>
document.getElmenentById('btn).addEventListener('click', function () {
  console.log('移动端的click事件被触发了')
}, false)

你会发现click事件也可以执行. 初看之下没有什么问题.

当你在移动端绑定touch事件时.

<button id="btn">在移动端相应touch事件的按钮?</button>
document.getElmenentById('btn).addEventListener('touchstart', function () {
  console.log('移动端的touch事件被触发了')
}, false)

touch 事件同样会被触发,也没有什么问题.

当你同时给一个元素绑定上 touch & click 事件时

<button id="btn">在移动端相应touch事件的按钮?</button>
document.getElmenentById('btn).addEventListener('click', function () {
  console.log('移动端的click事件被触发了')
}, false)
document.getElmenentById('btn).addEventListener('touchstart', function () {
  console.log('移动端的touch事件被触发了')
}, false)

你会发现:

  • 在PC端,仅会触发 click 事件.舍弃 touch 事件.

  • 在移动端总会先触发 touch 事件,接着在触发 click 事件.

  • 如果你开启了理想视口 (<meta name="viewport" content="width=device-width, initial-scale=1.0">), touchclick 事件触发之间的间隔大致在100ms左右. 根据不同的机型,可能有区别.

    理想视口开启,我的机器延迟在80ms左右
  • 如果你没开启理想视口, touchclick 之间的触发间隔大致在 300ms 左右.(每个机型差异不大)
没有开启理想视口,延迟300ms左右,各机型差距不大

结论

  • 移动端同时支持 touchclick 事件.
  • PC端仅支持 click 事件,而舍弃 touch 事件.
  • 移动端总是先触发 touch 在触发 click.
  • 当你没开启理想视口时, touchclick 之间的调用时间差是 300ms
  • 当你开启了理想视口时, touchclick 之间的调用时间差大致在 40-100ms 之间.

这就是别人口里说的,移动端 300ms 延迟问题的 <span style="color:red;"> ([一半])<span>.

为什么说是 <span style="color:red;">一半?</span>

  • 因为 addEventListener 本身就可以某个元素注册的某个事件注册多个回调函数.
  • 我们给 btn 注册了两次事件(一次 touch , 一次 click),它两在移动端当然后会全部执行.
  • 只不过是否开启安全视口会影响他两之间的调用延迟.

点击穿透问题

  • 点击穿透的问题核心是,移动端浏览器,在触发了一个元素的 touch 事件后,会在(<span style="color:red;">20-100ms或者300ms后</span>)接着触发此元素绑定的 click 事件(如果有)
  • 如果没有 click,那就完事大吉,啥也不干.到此结束.
  • 但是如果,绑定 touch 事件的元素式一个[正好]弹窗,在 touch 完之后,[正好]就隐藏消失. 但是后续在<span style="color:red;">20-100ms或者300ms后</span>调用click 这件事情,仍然在进行.
  • 如果正好,弹窗下面有一个元素绑定了click或者本身就有默认的click相应(比如a标签), <span style="color:red">此时也在寻迹click的延迟范围内</span>,那么点击穿透的问题就来了.

所以,点击穿透问题,是一个天时地利人和都得满足情况下,才会产生的问题.

  • touch元素点击完后消失.
  • 元素后面(视觉上),正好有一个元素绑定了click 或者有默认的click 行为(a标签,表单的submit按钮).
  • 点击穿透问题产生了.

一个DEMO说明点击穿透问题.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>02_移动端点击穿透</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    a {
      display: block;
      height: 100vh;
      width: 100vm;
      /* background-color: antiquewhite; */
      text-align: center;
      line-height: 80vh;
    }

    #mask {
      position: fixed;
      left: 0;
      top: 0;
      width: 100%;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.2);
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .btn {
      width: 200px;
      height: 40px;
      background-color: seagreen;
      color: #fff;
      border: none;
      outline: none;
    }
  </style>
</head>

<body>
  <a href="https://www.baidu.com">去百度</a>
  <div id="mask">
    <button class="btn">点我关闭</button>
  </div>
</body>
<script>
  // 1. 上层touch事件[正好]让元素消失.
  // 2. 下层也[正好]有一个绑定了click,或者有默认click属性的元素(a标签).
  // 3. 当上层元素消失后,就会触发这个下层元素的click事件.
  const btn = document.querySelector('.btn')
  const mask = document.getElementById('mask')
  btn.addEventListener('touchstart', function () {
    mask.style.display = 'none'
  }, false)

</script>

</html>

运行起来,你就会发现,当弹窗消失后,就立马会出发A标签的click事件,从而进入百度.


点击穿透问题.gif

点击穿透问题的核心就很清楚了.

移动端浏览器,在触发了 touch 事件之后,会在某段延迟时间之后,继续触发 click 事件. 这个 click 事件可以触发在之前的元素上.如果之前的元素消失了,就会触发在此元素下面的元素上(视觉上,非结构上)


点击穿透的问题解决.

  • 你既然是开发移动端事件,那就不要用 click, 全部用 touch 事件( <span style="color:red"> touch事件之间没有点击穿透问题 </span>). 杜绝 touch 在后续延迟时间里 click 的问题.
  • 对于某些没有办法避免的原生 click 事件,比如 A标签, 你可以让弹窗不要立马消失,延迟至少 300ms.这样,即使弹窗消失了,但是因为寻找 click 的时间段已经结束.所以也不会出现问题.
  • 当然还有很多各种各样的解决方案,但核心就一点,不要出现 click. 如果避免不了,就让在找 click 的这段延时里,不让 click 元素暴露出来.

其实点击穿透问题,一般也就出现在弹窗和点击弹窗立马消失,且弹窗下面正好有click原生事件的元素情况下.
大多数情况下,你不必去专门考虑点击穿透问题,等遇到了,利用上述原则,去解决就好了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容