移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?
- 延时时间
- 为何存在
- Safari 双击缩放事件
- 为了判断用户是想点击链接还是双击缩放,所以等待300ms
- 300ms内再次点击,算双击;300ms外算单击
- 如何解决
- meta标签设置全局禁止缩放
<meta name="viewport" content="user-scalable=no,initial-scale=1,maximun-scale=1"/>
- css
touch-action: none
仅IE支持
- 指定相应的元素上能够触发的用户代理(浏览器)的默认行为。设置none就是阻止默认行为,就不用再进行300ms的判断了
- 封装 tap 函数
- 原理
- 监听
touchstart
和 touchend
事件,记录两者触发时间,如果小于 150ms 且在此过程中没有滑动过屏幕,就当做click事件,然后执行回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <!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', () => { if (Date.now() - startTime < 150 && !isMove) { cb && cb(); } startTime = 0; isMove = false; }) } tap(document.getElementsByClassName('btn')[0], () => { console.log('click'); }) </script> </body> </html>
|
- FastClick 库
- 原理
- 检测到 touchend 事件后,会通过DOM自定义事件来模拟click事件,并把浏览器在300ms后的真正的click事件阻止掉
1 2 3 4 5 6 7 8 9 10
| if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); }
div.addEventListener('click',function () {
})
|
能不能直接用 touchstart 代替 click?
不能,使用 touchstart 去代替 click 事件有两个不好的地方
- touchstart是手指触摸屏幕就触发,有时候用户只是想滑动屏幕, 却触发了touchstart事件,这不是我们想要的结果
- 使用touchstart事件在某些场景下可能会出现点击穿透的现象
- 点击穿透现象:
- 假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。
- 这是因为在移动端浏览器,事件执行的顺序是touchstart > touchend > click。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。
场景重现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <!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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <!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"> 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>
|