標籤:android style blog class c code
一、事件捕獲與冒泡
先扯一下事件的觸發流程,這個之後會用到。
DOM2級事件規定事件包括三個階段:
① 事件捕獲階段
② 處於目標階段
③ 事件冒泡階段
大概的流程就是事件從最外層一層一層往裡面傳遞(捕獲階段), 到達觸發事件的目標元素(目標階段),然後再一層一層往上冒泡(冒泡階段)。這個流程事件所經過的元素繫結的對應事件的接聽程式都會被觸發。例如對元素p的子項目c綁定點擊事件,點擊元素c後會先在p觸發點擊事件,使用dispatch觸發也是。
PS:事件偵聽通過target.addEventListener(type, listener[, useCapture])添加,useCapture參數代表是否在捕獲階段觸發事件。
二、click事件的300ms延遲
行動裝置中使用click事件和在PC端會有所不同,大多數基於觸摸的瀏覽器裝置,在點擊時都會有個300ms的事件觸發等待時間,原因在於單擊後面還有個雙擊縮放動作,這個涉及到觸摸裝置的手勢互動行為原生設計,裝置需要通過時間判斷是單擊還是雙擊。
對於這300ms的延遲,根據網上搜尋的資料,可通過幾個簡單的方法解決:
- 無視之。。。如果所涉及業務對點擊的反應時間要求不大,300ms其實是可以忽略的。
- 在 chrome 和 firefox 的移動版本中,禁用頁面縮放可消除該延遲。不過只相容這兩種不好。
- 在 chrome 32+ 中,設定 viewport 的寬度小於或等於物理裝置的寬度。因為只支援chrome 32+,所以也忽略。
- IE10+中設定CSS觸摸 action 屬性。以後其他瀏覽器可能也會支援。但目前只支援IE10+,所以也忽略。
- 使用touch事件而不是click事件。這樣必須可以解決延遲,但也因此引發了奇怪的問題,由此轉入本文。
三、zepto的tap事件
如果我們直接使用touchend事件來代替click事件,一個很明顯的不妥就是如果滑動到某個元素上放開手那麼就會觸發該元素的touchend事件,這明顯不跟點擊操作一致。另外一個問題即觸發touchend事件後,在該地區300ms後還會觸發一次click事件,如果一個元素touchend後消失,那麼其點擊地區底下的元素還會被觸發一次click事件,這個問題叫做點透,不過這可以通過event.preventDefault來阻止(預設行為)。
為了更好的類比點擊事件並解決行動裝置點擊的300ms延遲,zepto的touch模組自訂了一個tap事件,該事件原理即是使用touch事件來類比的。基本原理就是給元素繫結touchstart和touchend事件,然後判斷前後事件的觸發地區是否一致,是的話即觸發自訂的點擊事件。
看起來挺完美的,但事實上使用zepto的tap事件即使在事件回呼函數中使用了preventDefault還是會出現點透問題。究其原因,就是zepto在觸發事件的時候使用了定時器。在自訂事件被觸發之前預設行為早就發生了,因此阻止不了click事件的觸發。
暫時不管zepto,對於click事件,我們可以用另外一個外掛程式提高click事件的響應速度,這就是fastclick!
四、fastclick
Fastclick的基本思想就是在某個父元素上捕獲其子項目的touchstart與touchend事件,然後依然根據位置資訊判斷是否為點擊動作,如果是的話立即使用dispatchEvent手動派發子項目的click事件,從而縮短了響應事件,並且在touchend的時候使用preventDefault阻止預設事件,防止300ms後再次觸發click事件。
事情貌似就這麼解決了,但事實就是沒這麼完美。撇開手動派發事件帶來的效能損耗不說,在android上preventDefault阻止touchend觸發click事件還沒有用,即在android上只要使用了touchend,就一定會觸發click事件,翻閱了很多資料後發現這個問題無法解決,有人說touchstart的時候調用preventDefault可阻止click,但親測過不行。
這時候,就只能用比較猥瑣的方法來解決了。在fastclick判斷為點擊操作並觸發click事件時,我們給事件對象添加一個flag。從之前的內容可以得知click事件會先在外層容器上觸發,因此我們在根目錄上偵聽click事件,當事件對象存在flag時即不做操作,當不存在時即阻止其事件派發流程。這樣即元素繫結在元素上的點擊事件接聽程式只會被手動觸發的click事件觸發。
document.addEventListener(‘click‘, function (event) { if (event.myclick == true) { return true; } if (event.stopImmediatePropagation) { event.stopImmediatePropagation(); } else { event.propagationStopped = true; } event.stopPropagation(); event.preventDefault(); return true;}, true);
雖然使用fastclick可以解決click的延遲問題,但添加了許多額外的操作,效能上的損耗是無可避免的。因此如果對點擊操作的要求不是很高的話還是建議忽略那點延遲。