如何解決移動端擊穿(穿透)問題

在移動端開發的時候,咱們有時候會遇到這樣一個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。在這裏我貼一下解決方案的地址

相關文章
相關標籤/搜索