一、背景
博主所負責其中一個項目是web頁面,在移動端上出現了事件穿透問題,開發介紹問題原因后,發覺是移動web一個知識點,值得記錄一下。
二、click與300ms延遲
移動瀏覽器提供一個特殊的功能:雙擊(double tap)放大
300ms的延遲就來自這里,用戶觸碰頁面之后,需要等待一段時間來判斷是不是雙擊(double tap)動作,而不是立即響應click(單擊),等待的這段時間大約是300ms。
- 移動端touch事件提供:
touchstart
、touchmove
、touchend
,卻沒有tap支持; - 主流框架/庫都是手動實現了tap事件,以求消除300ms延遲,提高頁面響應速度
- 對於簡單的頁面,可以把
touchstart
或者touchend
當作tap來用,但存在一些問題,比如手指接觸目標元素,按住不放,慢慢移出響應區域,會觸發touchstart
事件執行對應的事件處理器(本不應該觸發),touchend
事件也存在類似的問題。 - 使用原生的
touch
事件存在點擊穿透的問題,因為click是在touch系列事件發生后大約300ms才觸發,混用touch
和click
肯定會導致事件穿透。 - 手機上響應
click
事件有300ms的延遲,會有響應慢/延遲的感覺,因此很多移動頁面會使用touch/tap事件。
三、tap事件
用過Zepto
或者KISSY
等移動端js庫的人肯定對tap
事件不陌生,我們做PC頁面時候綁定click
,相應地手機頁面就綁定tap
。但原生的touch事件本身是沒有tap的,js庫提供的tap事件都是模擬出來的。
Zepto
中對tap事件處理:如果在touched響應250ms無操作后,則觸發singleTap。即事件觸發順序為:touch-tap-click。
三、點擊穿透問題
點擊穿透現象有3種:
- 點擊穿透問題:點擊蒙層(mask)上的關閉按鈕,蒙層消失后發現觸發了按鈕下面元素的click事件。
- 解析:蒙層的關閉按鈕綁定的是touch事件,而按鈕下面元素綁定的是click事件。touch事件觸發之后,蒙層消失了,300ms后這個點擊的click事件fire,event的target自然就是按鈕下面的元素。
- 跨頁面點擊穿透問題:如果按鈕下面恰好是有href屬性的a標簽,那么頁面就會發生調整。因為
a標簽跳轉默認是click事件觸發
。原因同上。 - 第三種:這次沒有mask,直接點擊頁面內按鈕跳轉至新頁,然后發現新頁面中對應位置元素的click事件被觸發了。
- 解析:和蒙層的道理一樣,js控制頁面跳轉的邏輯如果是綁定在touch事件上的,而且新頁面中對應位置的元素綁定的是click事件,而且頁面在300ms內完成了跳轉,三個條件同時滿足,就出現這種情況。
四、解決方案
思路:
-
不要混用touch和click:touch之后300ms會觸發click,只有touch或者只用click就自然不會存在問題。
-
吃掉/消費掉touch之后的click
依舊使用tap,只是在可能發生點擊穿透的情形下做額外的處理,拿個東西來擋住、或者tap后延遲350ms再隱藏mask、pointer-events、在下面元素的事件處理器里做檢測。
我們對事件響應速度要求不高,最后是通過延遲隱藏/關閉mask/彈窗解決的,簡單可控哈。