fastclick解析与ios11.3相关bug原因分析
最近發現升級到ios11.3之后,輸入框點擊變得不靈敏,第二次點擊頁面中的輸入框需要長按一會才能正常喚起鍵盤輸入。排查后,懷疑是fastclick出現了問題,上github看了issues,果不其然很多人也出現相同問題(https://github.com/ftlabs/fas... )。按照issues上的解決方法,也順利地解決了問題,不過,究竟為何會出現這么奇怪的bug?我們還需要繼續深入尋找答案。
首先,fastclick究竟用來干嘛?
簡而言之,它是用來解決300ms延遲和點擊穿透這兩個問題。
在移動設備上點擊按鈕后,瀏覽器將會等待300ms,繼續監聽點擊動作來判斷是否為雙擊事件,這就是300ms延遲問題。
為了解決這300ms的延遲問題,一種解決方案是將touch系列事件綁定在document上,通過計算touch事件觸發的時間位置等來判斷是否為移動設備的點擊,如zepto.js中自定義的tap事件;另一種方案,也是fastclick中的實現方案,當檢測到touchend事件的時候,會通過DOM自定義事件立即出發模擬一個click事件,并用preventDefault阻止300ms之后真正的click事件。
那么什么是點擊穿透問題?
點擊穿透問題是當兩個元素重疊在同一個位置,上層元素綁定touch事件,下層元素綁定click事件,當上層元素觸發touch事件后,可能會觸發下層div的click事件。
其次,fastclick都做了哪些工作?
fastclick的主要工作可見參考文獻[2]中的圖,如下:
fastclick的主要工作是在body或者頂層元素中綁定touch相關事件,在touch相關事件中標記手勢的位置與時間,根據此信息攔截click事件并判斷是否模擬觸發。
在處理300ms延遲的過程中,主要工作是模擬并攔截真正的click事件。
首先,攔截點擊事件的思路是將元素的onclick事件置為空,并用addEventListener重新綁定,理由是onclick將會在fastclick模擬的點擊事件之前觸發,在構造函數中關鍵代碼如下:
接著,看看fastclick如何判斷用戶的點擊事件是真正的點擊,在onTouchEnd事件中,判斷的關鍵代碼如下:
// event.timeStamp為touchend事件的事件,lastClickTime是上一次touchend事件的事件,此處判斷是否為雙擊操作 if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {this.cancelNextClick = true;return true; }// trackingClickStart是touchstart事件的事件,此處判斷是否為長按操作 if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {return true; }如果此次點擊是真正的點擊事件,有兩種情況要觸發模擬的click事件:一種是由needsFocus函數判斷是否為可以focus的元素,如<input type="text">、textarea等;另一種是由needsClick函數判斷是否為需要原生點擊的原生,不需要原生點擊的也需要模擬click事件,這部分的代碼邏輯比較簡單主要根據判斷元素的tagName和class來判斷,這里就不貼代碼了。
需要觸發模擬click事件的情況中,第一種情況(如輸入框等)是需要觸發focus事件的,觸發之后再觸發click事件,而第二種(如按鈕等)則單純觸發click事件即可。接下來,我們先分析focus事件的響應函數,再看模擬的click事件。
focus主要工作一方面在為了將光標移到移到輸入框尾部,另一方面觸發元素的focus事件,其響應函數為:
模擬的click事件,本質就是用代碼創建一個Event作為點擊事件觸發,關鍵代碼如下:
FastClick.prototype.sendClick = function(targetElement, event) {...// Synthesise a click event, with an extra attribute so it can be trackedclickEvent = document.createEvent('MouseEvents');clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);clickEvent.forwardedTouchEvent = true;targetElement.dispatchEvent(clickEvent); };最后,fastclick使用了preventDefault和stopImmediatePropagation攔截原生的click響應函數。preventDefault函數很常見了,但stopImmediatePropagation真是頭一次見它。根據規范可知,該方法不僅可以阻止冒泡,還能將元素綁定的后序相同類型事件的監聽函數的執行也一起阻止了,也就是說如果在點擊事件中調用了它,可以阻止點擊事件冒泡傳遞到父級元素,同時又能阻止該元素上的其他點擊響應函數。
最后,如何修復?
Issues中給出的修復方法是強制元素focus,即在改寫的focus響應函數中直接觸發元素的focus事件:
FastClick.prototype.focus = function(targetElement) {targetElement.focus(); };推測原因是由于ios11.3取消了input元素setSelectionRange自動聚焦的功能(非此原因 (⊙﹏⊙))
(6.22更新) 對比了一下ios11.3與之前的fastclick相關運行過程,只有320行左右有區別,“document.activeElement.blur();”,ios11.3之后在第二次點擊時有經過,而ios11.3之前的沒有。另外,350行左右的“targetElement.setSelectionRange(length, length);”,是引起輸入框聚焦的原因,但僅僅執行這個函數還無法到達聚焦的效果,fastclick還做了哪些相關工作,仍未知。此外,ios11.3支持了Web API:允許對事件支持被動模式,減少滾動屏幕的性能損耗和奔潰,并且針對document的touch事件監聽添加被動模式的配置,因此document將不再調用preventDefault方法。這些改動會引起fastclick的另一個bug,當靜置app或鎖屏幾秒后頁面將無法響應任何點擊操作。
解決方法也很簡單,只需去除被動模式,如下:
參考文獻
總結
以上是生活随笔為你收集整理的fastclick解析与ios11.3相关bug原因分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab交交变频,基于Matlab/
- 下一篇: 《乐高EV3机器人搭建与编程》——1.6