我们都知道,在有了移动端之后,就多了以 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
事件. -
如果你开启了理想视口 (
理想视口开启,我的机器延迟在80ms左右<meta name="viewport" content="width=device-width, initial-scale=1.0">
),touch
和click
事件触发之间的间隔大致在100ms
左右. 根据不同的机型,可能有区别.
- 如果你没开启理想视口,
touch
和click
之间的触发间隔大致在300ms
左右.(每个机型差异不大)
结论
- 移动端同时支持
touch
和click
事件. - PC端仅支持
click
事件,而舍弃touch
事件. - 移动端总是先触发
touch
在触发click
. - 当你没开启理想视口时,
touch
和click
之间的调用时间差是300ms
- 当你开启了理想视口时,
touch
和click
之间的调用时间差大致在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原生事件的元素情况下.
大多数情况下,你不必去专门考虑点击穿透问题,等遇到了,利用上述原则,去解决就好了.
网友评论