最近公司新開了一條業務線,有幸和大佬們一塊兒從頭開始構建一套適合新業務的框架。俗話說得好呀,適合本身的纔是最好的 😎。在新項目的 CodeReview 的時候,被大哥提到有沒有添加 fastClick 解決移動端 300ms 延遲的問題。如下就帶你追溯移動端延遲的 前世
此生
。javascript
國外有一篇關於 300ms 延遲的文章:What Exactly Is..... The 300ms Click Delaycss
世間萬物皆有因果,網頁興起於桌面端,那時候有誰會想到手機等移動設備的風靡?猶記得上大學那會兒,手機訪問學校網站的時候都是經過手指縮放來控制的 🙃,內心真的是一萬頭草泥馬奔騰而過,後來爲了解決移動端適配的問題,提出了 viewport
的解決方案,基於 無障礙(accessibility)(須要代理)交互設計師爲了更好的用戶體驗,特意提供了 雙擊縮放 的手勢支持。卻不知這正是一切禍亂的根源。html
谷歌有開發者文檔: 300ms tap delay, gone away(須要代理)前端
如下是原文的部分引用java
For many years, mobile browsers applied a 300-350ms delay between touchend and click while they waited to see if this was going to be a double-tap or not, since double-tap was a gesture to zoom into text.android
大體是說,移動瀏覽器 會在 touchend
和 click
事件之間,等待 300 - 350 ms,判斷用戶是否會進行雙擊手勢用以縮放文字。web
Ever since the first release of Chrome for Android, this delay was removed if pinch-zoom was also disabled. However, pinch zoom is an important accessibility feature. As of Chrome 32 (back in 2014) this delay is gone for mobile-optimized sites, without removing pinch-zooming! Firefox and IE/Edge did the same shortly afterwards, and in March 2016 a similar fix landed in iOS 9.3.瀏覽器
從上面咱們能夠獲取到幾個很是重要的信息:首先,谷歌就開始吹啦,自打咱們移動版 Chrome 發佈以來,只要你把縮放禁用掉,這個延遲就不會出現。不得不吹一波 Google,真的是甩開 Apple 幾條街,fastClick 源碼大部分都是用來解決 iOS 各個版本各類奇奇怪怪的 BUG。說實話,有些源碼我也不是很理解,可是咱啥也不敢說,啥也不敢問啊 😂。其次,Chrome 32 對移動端進行了優化,能夠不由用縮放,也能解決延遲的問題。接着 Firefox 和 IE/Edge 緊隨其後也修復了這個 BUG,最後,就是 iOS 9.3 也一樣修復這個 BUG (親測的確修復了)。app
如下能夠經過 hack 技巧,不添加 fastClick 也能修復延遲的問題框架
<meta name="viewport" content="user-scalable=no" />
複製代碼
或者
html {
touch-action: manipulation;
}
複製代碼
html {
touch-action: manipulation; // IE11+
-ms-touch-action: manipulation; // IE10
}
複製代碼
<meta name="viewport" content="width=device-width" />
複製代碼
經測試,若是不添加 width=device-width
不論是 Android 仍是 iOS 在已修復的版本中仍然會出現延時的問題。
上面說了這麼多,都是針對移動端瀏覽器的,既然是提到移動端,WebView 固然不得不說啦。
Android WebView 中 300ms 的延遲問題和移動端瀏覽器解決思路一致。
In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView. Additionally, consider setting the WKPreferences property javaScriptEnabled to false if you render files that are not supposed to run JavaScript.
iOS WebView 就有點讓人頭疼了。由於 iOS 8 以前一直都是 UIWebView,iOS 8 出了個新秀 WKWebView,那麼 iOS 9.3 300ms 延遲的 BUG 修復到底幹了啥呢?在客戶端 iOS 小姐姐的幫助下,最終的測試結果是 UIWebView 300ms 延遲的問題到如今一直存在,哪怕是最新的 iOS 版本(這大概這就是爲何老外推薦使用 WKWebView 而非 UIWebView,估計是不想修 BUG 了吧 😂),可是 WKWebView 在 iOS 9.3 的時候將這個問題給修復了。也就是說 iOS 9.3 以前 WKWebView 仍然是存在 300ms 延遲的問題的(忙活了半天,總算把全部的都給理清楚了 🙄)。
這部分可能有點爛大街了,網上一搜一大把,再說也沒啥意思,我就挑點我的以爲有意思的說一下 😁。
首先,講一下 fastClick 的實現原理吧,MDN 上 同時支持觸屏事件和鼠標事件 也有提到。
移動端,當用戶點擊屏幕時,會依次觸發 touchstart
,touchmove
(0 次或屢次),touchend
,mousemove
,mousedown
,mouseup
,click
。 touchmove
。只有當手指在屏幕發生移動的時候纔會觸發 touchmove
事件。在 touchstart
,touchmove
或者 touchend
事件中的任意一個調用 event.preventDefault
,mouse
事件 以及 click
事件將不會觸發。
fastClick 在 touchend
階段 調用 event.preventDefault
,而後經過 document.createEvent
建立一個 MouseEvents
,而後 經過 eventTarget.dispatchEvent
觸發對應目標元素上綁定的 click
事件。
JavaScript
(Maybe)首先,咱們須要明確一個問題,300ms 的延遲只有在移動端纔會出現,PC 端是沒有的。fastClick 中又有個一 notNeeded
的函數是用來判斷有沒有必要使用 fastClick。剛開始的時候,剛開始我閱讀完代碼表示對沒有進行移動端和 PC 端的區分表示不滿。不事後來一段不起眼的代碼改變了個人見解。
// Devices that don't support touch don't need FastClick
if (typeof window.ontouchstart === 'undefined') {
return true;
}
複製代碼
PC 端是沒有 touch
事件的所以 window.ontouchstart
返回 undefined
,移動端若是沒有綁定事件則返回 null
。果真只能證實我仍是太過年輕 🤣。
閱讀源碼期間,無心中發現使用事件委託時,Safari 手機版會有一個 bug,當點擊事件不是綁定在交互式的元素上(好比說 HTML 的 div),而且也沒有直接的事件監聽器綁定在他們自身。不會觸發 click
事件。具體能夠參考 click 瀏覽器兼容性
解決方法以下:(請原諒我厚顏無恥地直接搬過來了 😝)
cursor: pointer
的樣式,使元素具備交互式點擊onclick="void(0)"
的屬性,但並不包括 body 元素<a>
,代替不可交互式元素如 divevent.stopPropagation
只會阻止相同類型(event.type 相同)事件傳播,上面有提到過 移動端 觸摸事件觸發的順序問題,假如 我在 touchstart
中調用了 event.stopPropagation
只會 阻止後續 event flow 上其餘 touchstart
事件,並不會阻止 touchmove
,touchend
等 mouseEvent 事件的發生。
event.stopPropagation
,event.stopImmediatePropagation
的區別你真的知道嗎 🧐,event.stopPropagation
阻止捕獲和冒泡階段中當前事件的進一步傳播。若是有多個相同類型事件的事件監聽函數綁定到同一個元素,當該類型的事件觸發時,它們會按照被添加的順序執行。若是其中某個監聽函數執行 event.stopImmediatePropagation
方法,則當前元素剩下的監聽函數將不會被執行。
eventTarget.dispatchEvent
仍然會觸發完整的 event flow,而不只僅觸發 eventTarget 自己註冊的事件。
總的來講,閱讀源碼的過程是一次自我修煉的過程,是對過去某些不足的完善,其實就是發現本身很菜 😂,前方道險且長,同志們仍需努力呀 🤜。
我的以爲閱讀優秀的源碼是一件很幸福的事,由於它能潛移默化的提高你的審美
能力。但同時咱們也要帶有挑剔的眼光,找出當中存在的不足之處
fastClick 中 notNeeded
函數總的來講,已經至關不錯了,可是美中不足的是,對於 iOS 9.3 以上 使用 WKWebView 的用戶來講,引入 fastClick 無疑是畫蛇添足,還有可能致使某些潛在的問題。對於處女座的我來講,這一點是不能忍受的。不過單純的經過 UA
是沒法區分 UIWebView
和 WKWebView
的。不過若是頁面是在本身 App 中話,能夠經過在 UA
中攜帶 WebView
的信息來決定是否加載
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style></style>
</head>
<body>
<div>
<label for="userAgent">userAgent:</label>
<span id="userAgent"></span>
</div>
<div>
<label for="touchstart">touchstart:</label>
<span id="touchstart"></span>
</div>
<div>
<label for="touchend">touchend:</label>
<span id="touchend"></span>
</div>
<div>
<label for="click">click:</label>
<span id="click"></span>
</div>
<div>
<label for="diffClickTouchend">diff click - touchend:</label>
<span id="diffClickTouchend"></span>
</div>
<div>
<div id="test">test</div>
<div id="diff">diff</div>
</div>
<script> var userAgent = document.getElementById('userAgent'); userAgent.innerText = window.navigator.userAgent; var test = document.getElementById('test'); var diff = document.getElementById('diff'); var touchstart = document.getElementById('touchstart'); var touchend = document.getElementById('touchend'); var click = document.getElementById('click'); var diffClickTouchend = document.getElementById('diffClickTouchend'); test.addEventListener('touchstart', function(e) { touchstart.innerText = Date.now(); }); test.addEventListener('touchend', function(e) { touchend.innerText = Date.now(); }); test.addEventListener('click', function(e) { click.innerText = Date.now(); }); diff.addEventListener('click', function() { diffClickTouchend.innerText = click.innerText - touchend.innerText; }); </script>
</body>
</html>
複製代碼
回眸歷史,不能否認 fastClick 在解決移動端 300ms 延遲的問題上的確做出傑出的貢獻,不過 9102 的今天,是否仍然有必要使用呢,回到開始,我說過,適合本身的纔是最好的,所以,若是你的業務需求,是隻須要對 iOS 9.3 以上的 WKWebView 作適配,那麼強烈建議你不去使用,畢竟減小了文件請求大小,引入風險的機率。
最後,引用一句名言 老兵不死,只是凋零
向 fastClick 致敬。
文 / lastSeries
做者也在掘金哦,快關注他吧!
編 / 熒聲
本文由創宇前端做者受權發佈,版權屬於做者,創宇前端出品。 歡迎註明出處轉載本文。文章連接:juejin.im/post/5cdf84…
本文是創宇前端相關帳號最後一次由熒聲負責。終於,咱們一塊兒走到了一個故事的結束。
感謝您的閱讀,以及長期以來的支持。
再見啦。