300ms延遲由來
2007 年初。蘋果公司在發布首款 iPhone 前夕,遇到一個問題:當時的網站都是為大屏幕設備所設計的。于是蘋果的工程師們做了一些約定,應對 iPhone 這種小屏幕瀏覽桌面端站點的問題。這當中最出名的,當屬雙擊縮放(double tap to zoom),這也是會有上述 300 毫秒延遲的主要原因。
雙擊縮放,顧名思義,即用手指在屏幕上快速點擊兩次,iOS 自帶的 Safari 瀏覽器會將網頁縮放至原始比例。 那么這和 300 毫秒延遲有什么聯系呢? 假定這么一個場景。用戶在 iOS Safari 里邊點擊了一個鏈接。由于用戶可以進行雙擊縮放或者雙擊滾動的操作,當用戶一次點擊屏幕之后,瀏覽器并不能立刻判斷用戶是確實要打開這個鏈接,還是想要進行雙擊操作。因此,iOS Safari 就等待 300 毫秒,以判斷用戶是否再次點擊了屏幕。 后來,其他移動瀏覽器都復制了 iPhone Safari 瀏覽器的多數約定,包括雙擊縮放。
研究表明當延遲超過 100 毫秒,用戶就能感受到界面的卡頓。 然而,出于對手指觸摸滑動的區分,移動端頁面對于觸摸事件會有 300 毫秒的延遲,導致多數用戶感覺移動設備上基于 HTML 的 web 應用界面響應速度慢。瀏覽器開發商已經意識到這個問題,并已相繼提出了一些解決方案。
1、禁止縮放
上文提到,之所以有300ms延遲,主要是為了判斷用戶是否會在第一次點擊之后進行第二次點擊,如果第二次點擊與第一次點擊間隔小于300ms,則認為是縮放操作,因此,如果禁止頁面縮放,也就不需要等待300ms去判斷用戶的點擊香味是否為縮放操作,在移動開發中,可通過meta標簽禁止縮放
<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,maximum-scale=1">
這種方法通過完全禁用縮放避免了300ms延遲,卻大大降低了移動端頁面的可用性和可訪問性,比如,當你想要放大一張圖片或者一段字體較小的文本,卻發現無法完成操作。
2、 width=device-width Meta 標簽
除了雙擊縮放的約定外,iPhone 誕生時就有的另一個約定是,在渲染桌面端站點的時候,使用 980 像素的視口寬度而非設備本身的寬度。所以,一張寬度為320x320像素的圖片在設備寬度為320的iPhone4上并不占滿屏幕寬度,在移動段開發中,我們可以通過 <meta> 標簽來進行配置:
<meta name="viewport" content="width=device-width">
雙擊縮放的誕生解決了在移動設備上瀏覽桌面端站點的問題。既然站點內包含了 width=device-width 這一 <meta> 標簽,也就意味著這個網站采用了響應式設計,因此也就消除了在該站點上可能潛在的雙擊縮放需求。
這一解決方案的另一個關鍵之處在于它只是去除了雙擊縮放,但用戶仍可以使用雙指縮放 (pinch to zoom)。可見,縮放功能并非被完全禁用,也就不存在可用性和可訪問性的問題了。
3、指針事件 (Pointer Events)
指針事件最初由微軟提出,現已進入 指針事件是一個新的 web 事件系列,相應的規范旨在使用一個單獨的事件模型,對所有輸入類型,包括鼠標 (mouse)、觸摸 (touch)、觸控 (stylus) 等,進行統一的處理。例如,你可以只去監聽一個元素的 pointerdown事件,無需分別監聽其 touchstart和 mousedown事件。
有一個和點擊延遲直接相關的實現 —— 一個名為 touch-action的新 CSS 屬性。根據規范,touch-action屬性決定 “是否觸摸操作會觸發用戶代理的默認行為。這包括但不限于雙指縮放等行為”。從實際應用的角度來看,touch-action 決定了用戶在點擊了目標元素之后,是否能夠進行雙指縮放或者雙擊縮放。因此,這也相當完美地解決了 300 毫秒點擊延遲的問題。
touch-action的默認值為 auto,將其置為 none即可移除目標元素的 300 毫秒延遲。例如,下面的代碼在 IE10 和 IE11 上移除了所有鏈接和按鈕元素的點擊延遲。
a[href], button {
-ms-touch-action: none; /* IE10 */
touch-action: none; /* IE11 */
}
但就目前而言,只有 Internet Explorer 實現了指針事件,不過近期 Chrome 也宣布了將在未來的版本中提供支持。
當前解決方案
盡管瀏覽器開發商針對 300 毫秒延遲問題提出了一些解決方案,但目前并沒有簡單通用的方案。不過,已經有好多開發者考慮過這一問題,并帶來了一些基于 JavaScript 的跨平臺解決方案。這些方案可以歸為兩類 —— 針對指針事件的 polyfill 和“快速點擊 (fast click)”。
1、polyfill
指針事件的 polyfill 比較多,以下列出比較流行的幾個。
Google 的 Polymer
微軟的 HandJS
@Rich-Harris 的 Points
為避免 300 毫秒點擊延遲,我們主要關心這些 polyfill 是如何在非 IE 瀏覽器中模擬 CSS touch-action 屬性的,這其實是一個不小的挑戰。由于瀏覽器會忽略不被支持的 CSS 屬性,唯一能夠檢測開發者是否聲明了 touch-action: none 的方法是使用 JavaScript 去請求并解析所有的樣式表。HandJS 也正是這么做的,但不管是從性能上來看還是其他一些復雜的方面,這都會遇到問題。
Polymer 則是通過判斷標簽上的 touch-action 屬性 (attribute),而非 CSS 代碼。下面的代碼展示了 Polymer 是如何在鏈接上模擬 CSS touch-action: none 屬性的。
<a touch-action="none">Google</a>
2、fastclick
FastClick 是 FT Labs 專門為解決移動端瀏覽器 300 毫秒點擊延遲問題所開發的一個輕量級的庫。簡而言之,FastClick 在檢測到 touchend事件的時候,會通過 DOM 自定義事件立即觸發一個模擬 click事件,并把瀏覽器在300 毫秒之后真正觸發的 click事件阻止掉。
FastClick 的使用方法非常簡單,在 window load 事件之后,在 <body>用FastClick.attach()即可。
window.addEventListener( "load", function() {
FastClick.attach( document.body );
}, false );