移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?

<!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>Document</title>
  </head>
  <body>
    <button class="btn">点我</button>
    <script>
      /**
      * 封装tap 解决移动端click事件300ms延时问题
      * @param ele 监听元素
      * @param cb 执行的回调函数
      */
      function tap(ele, cb) {
        let startTime = 0, // 触摸开始时间
            isMove = false; // 是否有滑动
        ele.addEventListener('touchstart', () => {
          // 记录开始触摸时间
          startTime = Date.now();
        });
        ele.addEventListener('touchmove', () => {
          // 有滑动 算拖拽 不算点击
          isMove = true;
        });
        ele.addEventListener('touchend', () => {
          // 从 touchstart 到 touchend 时间不超过 150ms 且没有滑动屏幕
          // 就算 click 事件
          if (Date.now() - startTime < 150 && !isMove) {
            cb && cb();
          }
          // 重置变量
          startTime = 0;
          isMove = false;
        })
      }

      // 调用
      tap(document.getElementsByClassName('btn')[0], () => {
        console.log('click');
      })
    </script>
  </body>
</html>
// 消除所有元素click延时
if ('addEventListener' in document) {
  document.addEventListener('DOMContentLoaded', function() {
  FastClick.attach(document.body);
  }, false);
}
// 当使用click事件时 不会再有300ms延时
div.addEventListener('click',function () {
	// 业务处理
})

能不能直接用 touchstart 代替 click ?

不能,使用 touchstart 去代替 click 事件有两个不好的地方

  1. touchstart 是手指触摸屏幕就触发,有时候用户只是想滑动屏幕, 却触发了 touchstart 事件,这不是我们想要的结果
  2. 使用 touchstart 事件在某些场景下可能会出现点击穿透的现象
    1. 点击穿透现象:
      1. 假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的 touchstart 事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了 click 事件。
      2. 这是因为在移动端浏览器,事件执行的顺序是 touchstart > touchend > click 。而 click 事件有 300ms 的延迟,当 touchstart 事件把B元素隐藏之后,隔了300ms,浏览器触发了 click 事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。

场景重现

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
  <title></title>
  <style type="text/css">
    * {
      margin: 0;
      padding: 0;
    }

    .item {
      position: absolute;
      left: 0;
      top: 0;
      width: 200px;
      height: 200px;
      background: pink;
      opacity: .5;
    }
  </style>
</head>

<body>
  <div class="item"></div>
  <a href="<http://www.bilibili.com>">B站</a>
</body>
<script type="text/javascript">
  window.onload = function () {
    /*
    	1.pc端的事件可以在移动端触发
    	2.PC端事件有300毫秒延迟
    	3.移动端事件不会有延迟

      当点击item和a标签重叠的部分时
      首先触发了移动端没有延迟的touchstart事件,
      调用了该事件的回调函数,隐藏了item
      接着300ms延迟以后原地触发了pc端click事件,
      此时由于item已经消失,所以“点”在了a标签上,
      导致跳转页面
    */
    var item = document.querySelector(".item");
    var a = document.querySelector("a");
    item.addEventListener("touchstart", function () {
    	console.log("touchstart");
      this.style.display = "none";
    })
    a.addEventListener("click",function(){
    	console.log("click");
    })

  }
</script>

</html>

解决方案

fastclick.js

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
  <title></title>
  <style type="text/css">
    * {
      margin: 0;
      padding: 0;
    }

    .item {
      position: absolute;
      left: 0;
      top: 0;
      width: 200px;
      height: 200px;
      background: pink;
      opacity: .5;
    }
  </style>
</head>

<body>
  <div class="item"></div>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
  <a href="<http://www.atguigu.com>">回娘家</a>
</body>
<script src="<https://cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js>"></script>
<script type="text/javascript">
  // 引入 fastclick.js
  if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
    	FastClick.attach(document.body);
    }, false);
  }
  window.onload = function () {
    var item = document.querySelector(".item");
    var a = document.querySelector("a");
    item.addEventListener("touchstart", function () {
      console.log("touchstart");
      this.style.display = "none";
    })
    a.addEventListener("click", function () {
      console.log("click");
    })

  }
</script>

</html>

🌈 fastclick 退出历史

*Note: As of late 2015 most mobile browsers - notably Chrome and Safari - no longer have a 300ms touch delay, so fastclick offers no benefit on newer browsers, and risks introducing bugs into your application. Consider carefully whether you really need to use it.

注意:自 2015 年末以来,大多数移动浏览器 - 特别是 Chrome和 Safari - 不再有300毫秒的触摸延迟,因此在更新的浏览器上使用 fastclick不会带来任何好处,并可能在应用程序中引入错误。请仔细考虑是否真的需要使用它。

—* https://github.com/ftlabs/fastclick

阅读更多: