1.单击穿透原理
1 其一是单击穿透情况:单击蒙层(mask)上面的 “ 关闭 ” 按钮,如果蒙层消失,一定是触发了 按钮下面元素的click事件,让蒙层消失。
2.其二是页面单击穿透情况:如果按钮下面恰好是一个有 href 属性的a标签,也就是存在a标签,那么页面就会发生跳转。
3.其三跨页面单击穿透情况:该情况不要会出现蒙层,直接单击页面内按钮跳转至新页,之后在新的页面中可以看到对应位置元素的click事件被触发了。
2.为什么会出现
touchstart:在该DOM上(或者是冒泡到该DOM)上手指触摸开始即能立即触发。
click:在该DOM上(或者冒泡到该DOM)上手指触摸开始,且手指没有在屏幕上移动,且这个DOM元素上手指离开屏幕,且触摸和离开屏幕之间的间隔较短才能触发。
事件触发时间先后排序:touchstart => touchend => click。
遮盖层:遮盖层的点击即使有小的延迟也没有影响,相反能给用户更好的体验,针对遮盖层采用自己的click事件,不会出现点击穿透的问题。
3.解决方案
(1) 只用touch
该方法是一种较为简单的解决办法,能够完美地解决单击穿透问题,实行方案是把页面内所有的click事件都替换换成touch事件(touchstart、touched、tap)。
(2) 只用click
这个方法在单击会带来300ms的延迟,所以页面内任何一个自定义使用click时都将增加300ms的延迟。
(3) 轻触 (tap) 后延迟350ms再隐藏蒙层
这种方法相当于原先的操作改动最小,但缺点是隐藏蒙层变慢了,时间大约市350ms。
(4) 只用touch
这种方法比较麻烦并且有缺陷,不推荐去使用。蒙层隐藏后,给按钮下面的元素添上pointer-events:none样式,让click穿过去,350ms后去掉这个样式。恢复响应的缺陷是蒙层消失后的350ms内,用户单机按钮下面的元素没反应,如果用户单机速度很快,会发现这个不足的地方。
4.代码实现
4.1 最外层元素监听的触摸事件里,阻止默认行为。
btn.addEventListener("touchend",funtion(event){
event.preventDefault();
})
4.2 阻止所有元素的默认行为,document监听触摸事件。
document.addEventListener("touchstart",function(event){
event.preventDefault();
},{passive:false})
4.3 里面的元素使用没有点击特性的元素,比如用div代替a。
ass.forEach(function(item){
item.addEventListener("touchend",function(){
location.href = this.dataset.href;
})
})
// div中没有href属性,用的是data-href。在绑定监听时获取href
// 方式一:dataset.href要求属性data-href必须是data-
// location.href = this.dataset.href;
// 方式二:利用getAttribute获取自定义属性的值
// location.href = this.getAttribute("data-href")
4.4 利用css的pointer-event,设置为none
btn.addEventListener("touchend",function(){
// 先让a失去点击特性
ass.forEach(function(item){
item.style.pointerEvents = "none";
})
});
cover.style.display = "none";
// 遮罩层消失之后且click动作发生之后,让a具有点击特性
setInterval(function(){
ass.forEach(function(item){
item.style.pointerEventys = "auto";
});
},400)