如何解决移动端击穿(穿透)问题

在移动端开发的时候,咱们有时候会遇到这样一个bug:点击关闭遮罩层的时候,遮罩层下面的带有点击的元素也会被触发,给人一种击穿了页面的感受,这是为何呢?javascript

为了让你们更直观的看到效果,我复现了bug,并录制了一个gif。供你们参考:css

  • 点击“打开弹框”按钮,显示遮罩层
  • 点击“关闭弹框”按钮,遮罩层消失,底下的链接被触发

上图事例js部分代码html

var show = document.getElementById('show') // 打开按钮
var mask = document.getElementById('mask') // 遮罩层
var btn = document.getElementById('btn')   // 关闭按钮

show.onclick = function () {
    mask.style.display = 'block'
}

btn.addEventListener('touchstart', function () {
    mask.style.display = 'none'
}, false)

</body>
</html>

复制代码

这样问题的造成缘由是什么呢?

咱们先来看一段代码:(如下代码需在移动端上运行)java

<div id="btn">我是一个按钮</div>
复制代码
var btn = document.getElementById('btn')
btn.addEventListener('touchstart', function () {
	console.log('start')	
}, false)

btn.addEventListener('touchmove', function () {
    console.log('move')
}, false)

btn.addEventListener('touchend', function () {
    console.log('touchend')
}, false)

btn.addEventListener('click', function () {
    console.log('click')
}, false)

复制代码

以上代码会出现2种运行状况git

  • start ===> move ===> end
  • start ===> end ===> click

看到这里相信你们都明白了,因为「关闭弹框」按钮绑定的事件是touch,a标签是click事件,在touch事件触发后,咱们弹出框的遮罩层就消失了,这时候的click事件就被a标签给捕获到了,造成了击穿的效果。github

方法1、阻止默认事件

btn.addEventListener('touchend', function (e) {
    mask.style.display = 'none'
    e.preventDefault()
}, false)
复制代码

在执行 touchstart 和 touchend 事件时,隐藏执行完隐藏命令后,当即阻止后续事件(推荐在touchend时,阻止后续的默认事件)性能

方法2、统一使用click事件

btn.addEventListener('click', function () {
    mask.style.display = 'none'
}, false)
复制代码

这个方法简单,就是交互的效率没有click事件高,另外,用户在touch的时候,有可能微微滑动了一下,就会没法触发点击事件。影响用户体验。动画

方法3、延迟执行

btn.addEventListener('touchend', function () {
	setTimeout(function () {
        mask.style.display = 'none'  // 可使用fadeOut动画
    }, 300)
}, false)
复制代码

点击以后,咱们不当即隐藏。让遮罩在350ms毫秒内淡出消失。(我为了演示方便就没有添加动画了,采用了定时器方法。)ui

方法4、 css属性pointer-events

click.setAttribute('style', 'pointer-events:none')
mask.style.display = 'none'
setTimeout(function () {
    click.setAttribute('style', 'pointer-events:auto')
}, 350)
复制代码

这样作法是,在遮罩消失以前,先让a标签忽略点击事件,这样遮罩层的点击事件,就不会被a标签捕获到。仍是等350毫秒以后,再次赋予a标签的点击能力。这个方法跟方法三原理类似,只是利用了不一样的css属性而已。我的以为方法三比较好一点。方法四有明显的2个缺点:spa

一、遮罩层下面可能有多个带有事件的元素,那么你须要给全部可点击元素添加pointer-events属性 而后删除。不只容易出错,还影响性能

二、若是用户在350毫秒内点击了元素,会形成页面失效的错觉,影响体验。

方法5、fastClick库

这个库的引用方法,在我上一篇文章中已经讲到。fastClick的原理就是使用了方法一的作法。fastClick 在 touchend 阶段 调用 event.preventDefault,而后经过 document.createEvent 建立一个 MouseEvents,而后 经过 event​Target​.dispatch​Event 触发对应目标元素上绑定的 click 事件。

后来有读者提了一个fastClick的bug。在这里我贴一下解决方案的地址

相关文章
相关标签/搜索