問題
在做手機頁面的時候,遇到這樣一個問題:
點擊抽獎后,會出現一個彈框,點擊彈框上的"確定"按鈕,這時問題出現了,當確定按鈕和下邊的抽獎按鈕重合時,彈框隱藏,並且同時觸發了抽獎按鈕!!!
這時就了解到:什么是事件穿透?點擊上面的一層時會觸發下面一層的事件。
zepto的tap是通過兼聽綁定在document上的touch事件來完成tap事件的模擬的,tap事件是冒泡到document上觸發的。
在點擊完成時的tap事件(touchstart/touchend)需要冒泡到document上才會觸發。
而在冒泡到document之前,用戶手的接觸屏幕(touchstart)和離開屏幕(touchend)是會觸發click事件 的,因為click事件有延遲觸發 (這就是為什么移動端不用click而用tap的原因)(大概是300ms,為了實現safari的雙擊事件的設計)。
所以在執行完tap事件之后,彈出來的選擇組件馬上就隱藏了,此時click事件還在延遲的300ms之中,當300ms到來的時候,click到的其實不是完成而是隱藏之后的下方的元素,如果正下方的元素綁定的有click事件此時便會觸發,如果沒有綁定click事件的話就當沒click,但是正下方的是input輸入框(或者select選擇框或者單選復選框),點擊默認聚焦而彈出輸入鍵盤,也就出現了上面的點透現象。
解決
- 監聽touchend事件,並在事件中使用
preventDefault() 阻止冒泡。
$(".close").on("touchend", function(e){ //這里使用touchstart事件也可以
e.preventDefault();
//do something...
});
- 延遲一定的時間來處理事件。本人測試是超過320毫秒就不會出現穿透,與jquery的動畫(fadeIn(),fadeOut())等配合,感覺良好;
$(id).fadeIn(300);
- 如果還不奏效,終極解決方案是用click替代tap;
//設置點擊事件為_tap
_tap = touchend in document ? "touchend":"click"
//這樣在執行的過程中就可以直接調用
div.on(_tap, function(){...})
- 使用css3的pointer-events=true,pointer-events=none切換來實現
對pointer-events這個屬性進行簡單介紹:設置或檢索在何時成為屬性事件的target。
使用pointer-events來阻止元素成為鼠標事件目標不一定意味着元素上的事件偵聽器永不會觸發。
如果元素后代明確指定了pointer-events屬性並允許其成為鼠標事件的目標,
那么指向該元素的任何事件在事件傳播過程中都將通過父元素,並以適當的方式觸發其上的事件偵聽器。
當然位於屏幕上在父元素上但不在后代元素上的鼠標活動都不會被父元素和后代元素捕獲(將會穿過父元素而指向位於其下面的元素)。
補充
我們往往用a標簽來寫一些按鈕等,如何阻止a的鏈接跳轉呢?
disabled的屬性是一個禁止功能,但是在a標簽下不能真正的工作,在a標簽內只有沒有herf和pointer-events:none時,才能真正的禁用事件,無論是鼠標事件還是keyup事件。
附
阻止點擊穿透常用於移動端禁止觸摸滾動