移动端touch事件穿透原因及解决办法
2007 年初。苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设备所设计的。于是苹果的工程师们做了一些约定,应对 iPhone 这种小屏幕浏览桌面端站点的问题。这当中最出名的,当属双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。双击缩放,顾名思义,即用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。 那么这和 300 毫秒延迟有什么联系呢? 假定这么一个场景。用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。
click是在移动端有300ms的延迟,混用click和touch会导致穿透事件。
1.蒙层穿透
蒙层绑定了touch事件,而被蒙层覆盖的元素绑定的是click(或者tap,只要这个事件有延迟,都会发生),touch事件触发后,蒙层消失,300ms后由于“原先遮挡在前面的蒙层消失了”,click事件就落到了 被覆盖的元素上面
2.跨页面点击穿透
前面说的是被覆盖的元素添加了click事件,如果被覆盖的元素没有绑定click,而是一个a链接,或者是input输入框,也会发生穿透(a链接默认就有click跳转事件)
解决办法一:设置蒙层消失的延迟
tap后延迟350ms再隐藏mask。先把透明度设置为0,解决视觉层面的效果,在设置定时器延迟,让蒙层元素消失
$('.mask').on('touchstart',
function() {
console.log('mask-touchstart');
$(this).css('opacity', 0);
setTimeout(function() {
$('.mask').css('dispaly', 'none');
},
350);
})
解决办法二:pointer-events,让被覆盖元素短时间内无法触发click
有必要介绍一个 CSS3 的属性 —— pointer-events。
pointer-events: auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit;
pointer-events 属性有很多值,有用的主要是 auto 和 none,其他属性为 SVG 服务。
属性 | 含义 |
---|---|
auto | 默认值,鼠标或触屏事件不会穿透当前层 |
none | 元素不再是target,监听的元素变成了下层的元素(如果子元素设置成 auto,点击子元素会继续监听事件) |
$('.mask').on('touchstart',
function() {
console.log('mask-touchstart');
$(this).css('display', 'none');
//让被覆盖元素无法响应click
$('.box').css('pointer-events', 'none');
//恢复被覆盖元素
setTimeout(function() {
$('.box').css('pointer-events', 'auto');
},
300);
})
解决办法三:使用fastclick库
使用fastclick库,从此所有的点击事件都使用click,没有300ms的延迟,也没有穿透问题
//引入fastclick
<script src="js/fastclick.min.js"></script>
原生js初始化
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
zepto初始化
FastClick.attach(document.body);
上面的FastClick.attach(document.body);传递的是body元素,代表body元素包括它的子元素都效果,如果想要部分dom元素有这个效果,可以换成相应的don元素
//直接使用click绑定
$('.mask').on('click',function(){
console.log('mask-click');
$(this).css('display','none');
})