H5移動頁面的touch事件與點選穿透問題
先舉一個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width,height=device-height,initial-scale=1.0"> <title>Title</title> <style> *{padding:0;margin:0;}html,body{width: 100%;height: 100%;} .mask { position: absolute; width: 100%; height: 100%; background: rgba(26,26,26,0.2); } a{ display: block; width: 100%; height: 100%; font-size: 2rem; } </style> </head> <body> <div class="mask"></div> <a href="http://www.baidu.com">去百度</a> </body> <script src="jquery.js"></script> <script> $(document).on('touchstart', '.mask', function() { $(this).hide(); }) </script> </html>
簡介:上邊例子中,a標籤連結到百度,a標籤上方漂浮這一個蒙層,js的功能是實現點選蒙層後蒙層消失。
效果:點選蒙層,蒙層消失;300毫秒後,下邊的a標籤的click事件被觸發,網頁跳轉到百度,即發生了事件穿透。
把js程式碼換成以下寫法:
<script>
$(document).on('click', '.mask', function() {
$(this).hide();
})
</script>
效果:點選蒙層,蒙層消失,但存在300毫秒延遲。
解釋:
一.click與300ms延遲
移動瀏覽器提供一個特殊的功能:雙擊(double tap)放大
300ms的延遲就來自這裡,使用者碰觸頁面之後,需要等待一段時間來判斷是不是雙擊(double tap)動作,而不是立即響應單擊(click),等待的這段時間大約是300ms。
移動事件提供了touchstart、touchmove、touchend卻沒有提供tap支援,主流框架(庫)都是手動實現了自定義tap事件,以求消除300ms延遲,提高頁面響應速度。對於簡單的頁面,可以把touchstart或者touchend當作tap來用,但存在一些問題,比如手指接觸目標元素,按住不放,慢慢移出響應區域,會觸發touchstart事件執行對應的事件處理器(本不應該觸發),touchend事件也存在類似的問題。
此外,使用原生touch事件也存在點選穿透的問題,因為click是在touch系列事件發生後大約300ms才觸發的,混用touch和click肯定會導致點透問題,下面詳細介紹
二.點選穿透問題
①點選蒙層(mask),蒙層消失後發現觸發了按鈕下面元素的click事件,蒙層繫結的是touch事件,而按鈕下面元素繫結的是click事件(a預設為click事件),touch事件觸發之後,蒙層消失,300ms後這個點的click事件觸發,事件的target自然就是蒙層下面的元素,蒙層此時已經消失。
②如果按鈕下面恰好是一個有href屬性的a標籤,那麼頁面就會發生跳轉,因為a標籤跳轉預設是click事件觸發,所以原理和上面的完全相同。
三、解決方法
①只用touch:把頁面內所有click全部換成touch事件(touchstart、’touchend’、’tap’),需要特別注意a標籤,a標籤的href也是click,需要去掉換成js控制的跳轉,或者直接改成span + tap控制跳轉。如果要求不高,不在乎滑走或者滑進來觸發事件的話,span + touchend就可以了,畢竟tap需要引入第三方庫
缺點:不利於seo(因為沒有a標籤,內鏈外鏈之類的也就沒有了)
②只用click。缺點:會帶來300ms延遲。
③使用fastclick消除300毫秒延遲,隨後只用click。比較好用的解決方案,只是多載入了幾K的資源。
④遮擋:給mask的消失做一個fade效果,類似jQuery裡的fadeOut
,並設定動畫duration大於300ms,這樣當延遲的 click 觸發時,就不會“穿透”到下方的元素了。
注:程式碼中本人使用的是JQuery,如果是開發,推薦使用移動端的js庫zepto.js,用法跟JQuery差異不大,touchstart請換成tap(相當於pc端的click),但依然會存在延遲,解決方法如上。
tap:原生的touch事件本身是沒有tap的,js庫裡提供的tap事件都是模擬出來的,單次點選300ms內再次點選則判定為double tap。