移動端 click 事件 300ms 延遲的前世今生

原文首發(fā)于 baishusama.github.io氧猬,歡迎圍觀~

存疑

最開始瘫寝,我遇到的其實是“移動端遮罩層滑動穿透”的問題国旷。

在查找“滑動穿透”問題相關資料的時候薇芝,我搜到了很多 click 300ms 延遲的問題。我那個時候有些不知所云务傲,因為我自己并沒有真實遇到過 300ms 延遲現象凉当,也就沒怎么在意枣申。

時至今日想動筆總結遇到了若干次的“滑動穿透”問題的時候,搜集資料的偶然間得以解惑 300ms 的前世今生看杭。

移動端 click 的 300ms 延遲

那么忠藤,這 300ms 延遲到底是從哪里來的呢?

時間要追溯到 2007 年初代 iPhone 發(fā)布前夕楼雹,蘋果為了解決“如何用手機這種小尺寸屏幕來顯示 PC 端網頁”這個問題模孩,提出了很多聰明的約定(convention)。而后因為 iPhone 的大獲成功贮缅,這些約定被各大手機瀏覽器爭相效仿榨咐。

這些約定之中,雙擊縮放(Double Tap to Zoom) 就是 300ms 的“元兇”——當用戶在頁面上 click 的時候谴供,瀏覽器為了判斷這個用戶操作是單擊還是雙擊块茁,會等待 300-350ms 。如果 300ms 內桂肌,發(fā)生了第二次 click 事件数焊,那么視為雙擊;否則為單擊崎场,等 300ms 時間過去之后佩耳,才觸發(fā) click 事件。

在那個還不存在響應式設計和雙指縮放(Pinch to Zoom)的時代照雁,這個延遲是一個合理的預防措施蚕愤。但不幸的是,這 300ms 的延遲已經成為用戶覺得 web 應用比 native 應用更慢饺蚊、性能不及后者的主要原因之一萍诱。諸如,鏈接污呼、按鈕裕坊、多選框等基于 click 交互的元素,以及 JS 對 click 事件的監(jiān)聽燕酷,都因此受到影響籍凝。

幸運的是,瀏覽器開發(fā)商(vendor)和開發(fā)者都注意到了這個問題苗缩,提出了一些解決方案饵蒂。

解決方案

方案一、?? 禁用縮放

  • 代碼:
    <meta name="viewport" content="user-scalable=no">
    <!-- 或者 -->
    <meta name="viewport" content="initial-scale=1, minimum-scale=1, maximum-scale=1">
    
  • 原理:雙擊是為了縮放酱讶,如果禁用縮放退盯,那么就沒雙擊什么事兒了,也不需要額外等待 300ms 了。
  • 支持情況:在 Android 平臺上渊迁,由 Chrome 最先提出慰照,FireFox、Opera 等瀏覽器也相繼支持琉朽;IOS 9.3 開始一度支持毒租,IOS10 開始不再支持。
  • 缺點:Safari 不支持箱叁。而且墅垮,禁用縮放會損害移動端網頁的可用性和可訪問性。例如蝌蹂,可能無法放大網頁中的一張圖片或一段字體較小的文字噩斟。

這里要注意區(qū)分:“雙擊縮放”(Double Tap to Zoom)和“雙指縮放”(Pinch to Zoom)。為了兼顧消除 300ms 延遲和不損害可用性和可訪問性孤个,我們應該拋棄雙擊縮放、擁抱雙指縮放沛简。

方案二齐鲤、?? 視窗寬度設置為設備寬度

  • 代碼:
    <meta name="viewport" content="width=device-width">
    
  • 原由:正如 Chromium Code Reviews 上說的,viewportwidth 設置得小于等于 device-width 的頁面椒楣,是針對移動端優(yōu)化過的或者是響應式的站點给郊,其內容足夠清晰,雙擊縮放失去了意義捧灰。因此淆九,為包含上面這行代碼的頁面禁用雙擊縮放。同時毛俏,雙指縮放得以保留炭庙,從而也就沒有可用性和可訪問性問題了。
  • 支持情況:自 Chrome 32 開始煌寇,FF焕蹄、IE/Edge 也隨后支持了;2016 年 3 月阀溶,IOS 9.3 開始支持腻脏。
  • 推薦使用!

該解決方案的“禁止雙擊縮放”是遵守如下規(guī)則的:

  • 當頁面設置了視窗寬度為設備寬度且是初始尺寸(頁面尚未縮放)银锻,此時永品,雙擊縮放才是被禁止的。
  • 如果視窗尺寸不是初始尺寸(頁面已經縮放)击纬,雙擊縮放是被允許的鼎姐。
  • 為了在用戶結束縮放后仍能 fast-click ,縮小時,只能縮小到初始尺寸症见,而不是最小尺寸喂走。

方案三、?? 指針事件(Pointer Events)

  • 代碼:
    a, button, .myelements {
        -ms-touch-action: manipulation; /* IE10  */
        touch-action: manipulation;     /* IE11+ */
    }
    
  • 根據規(guī)范:CSS 屬性 touch-action 決定了觸摸輸入(touch input)能否觸發(fā) UA (User Agent)支持的默認行為谋作。這包括但不限于諸如平移或縮放等行為芋肠。
  • 根據 MDNtouch-actionmanipulation 值激活了平移和雙指縮放手勢,而禁用了雙擊縮放等非標準的手勢遵蚜。
  • 支持情況:在 Can I Use 上可以看出帖池,除了 Opera Mini 不支持、FF 需要手動啟用和 Android 4.x 的自帶瀏覽器有些迷之外吭净,其他瀏覽器支持良好睡汹。
  • 推薦使用!

在只有 IE 支持指針事件的初期寂殉,誕生了不少指針事件的 polyfill 解決方案囚巴。在仍不支持指針事件的瀏覽器上,這是一種變通的方式友扰。

shim VS polyfill

  1. 一個 shim 是一個庫彤叉,它將一個新的 API 引入到一個舊的環(huán)境中,而且僅靠舊環(huán)境中已有的手段實現村怪。
  2. polyfill 就是瀏覽器 API 的 shim 秽浇。 它用于實現瀏覽器并不支持的原生 API 的代碼,是抹平新舊瀏覽器對原生 API 支持差異的封裝甚负。通常柬焕,polyfill 會先檢查當前瀏覽器是否支持某個 API,如果不支持的話就加載它自己的實現梭域,然后新舊瀏覽器就都可以使用這個 API 了斑举。相當于“打補丁”,“刮膩子”碰辅。

方案四懂昂、?? 輕量級庫 FastClick

  • 代碼:
    window.addEventListener( "load", function() {
        FastClick.attach( document.body ); // 直接綁定到 <body> 上可以確保整個應用都能受益
    }, false );
    
  • 原理:FastClick 在檢測到 touchend 事件的時候,會通過 DOM 自定義事件立即觸發(fā)一個模擬的 click 事件没宾,并把瀏覽器 300ms 之后真正觸發(fā)的 click 事件阻止掉凌彬。
  • 無沖突:當 FastClick 檢測到當前頁面使用了基于 <meta> 標簽或者 touch-action 屬性的解決方案時,會靜靜地看別的解決方案裝逼循衰。
  • 唯一的缺點:文件大小占 10 KB……
  • 推薦使用铲敛!

關于“始作俑者” Safari

起承轉折

(2013) 300 毫秒點擊延遲的來龍去脈一文中提到的 IOS 特有的雙擊滾動(Double Tap to Scroll):仍存在拳芙、并沒有像原文猜測的那樣消失尔觉。(親測 IOS 10.2.1 Safari 已設置 <meta name="viewport" content="width=device-width"> 的頁面在屏幕上或下 1/4 處雙擊仍能滾動。)

起初看到「2016 年 3 月發(fā)布的 IOS 9.3 移除了 300ms 延遲犯建、從而實現了“fast-tap” 」時,我還欣慰地想道:最先提出“雙擊縮放”約定的蘋果先鱼,在最后也順應了歷史潮流嘛俭正。但是接著看到「IOS10 無視了禁用縮放(user-scalable)」我的內心瞬間黑人問號:“?焙畔?掸读?”。

后來宏多,靜靜地看了兩篇文章(Safari zoom gesture's comeback in iOS 10How to disable viewport scaling in iOS 10? You don't.)儿惫,做了點 <meta> 標簽的測試。

測試結果

測試環(huán)境:IOS 10.2.1

  1. 只設置 <meta name="viewport" content="user-scalable=no"> 和不設置沒有任何區(qū)別——user-scalable=no 被完全無視伸但。
  2. 只設置 <meta name="viewport" content="width=device-width"> 肾请,和方案二里的描述一致,仍可以在初始尺寸下禁用雙擊縮放更胖。
  3. 只設置 <meta name="viewport" content="initial-scale=1.0"> 铛铁,初始狀態(tài)和“測試2”很像,但是仍存在雙擊縮放却妨,即仍有 300ms 延遲避归。
  4. 設置 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 或者 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> ,和只設置 width=device-width 并無顯著差異管呵。
  5. 為某個 <a> 鏈接設置 touch-action: manipulation; ,可以禁用該元素上的雙擊縮放哺窄。

要點如下:

  1. user-scalable=no 完全起不到禁止縮放的作用捐下,width=device-width 仍能且僅能禁止雙擊縮放。
  2. 只設置 meta 無法完全禁用縮放萌业,雙指縮放總是可行的坷襟。

暮然回首

冷靜下來后,重新審視上述變故生年,發(fā)現其實是兩回事婴程。前面提到過“我們應該拋棄雙擊縮放、擁抱雙指縮放”抱婉,蘋果沒有打破這個原則档叔。只是,蘋果出于可訪問性考慮蒸绩,直接任性地完全無視了 user-scalable=no衙四。

Accessibility
Pinch-to-zoom is always enabled for all users. The viewport setting for user-scalable is ignored.

當然,這導致了覺得應該一切盡在掌控患亿、想要完全禁用縮放以避免破壞布局的開發(fā)者的怨言传蹈。如果,你還是想完全禁用縮放,可以參考 SO 上的這個回答惦界。

解惑

最開始提到過挑格,我至今沒有遇到過這個問題。對這個現象我推理如下:

我的腎機在開發(fā)移動端的半年間只在近期做過一次系統升級(目前已升到 10.2.1)沾歪。之前使用的具體的版本號已經無從得知了(P.S. 如果有誰知道怎么查看腎機本機上的版本更新歷史漂彤,請務必告訴我233),但是更新到 IOS10 之前瞬逊,我一直有使用 9.3+ 才支持的 Night Shift 功能显歧,也就是說升級之前的系統版本號肯定在 9.3 或者以上。

而我寫移動端頁面的時候确镊,慣例會 meta:vp 然后 Tab 生成 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">士骤。在 IOS10 之前,這行代碼還是能夠禁用頁面的縮放的蕾域,也就不存在 300ms 的延遲問題了拷肌。

這就是為什么之前我本機測試的時候一直沒有遇到傳說中的 300ms 延遲現象的原因了。

參考

  1. (2013) 300 毫秒點擊延遲的來龍去脈
  2. (2014) 5-ways-prevent-300ms-click-delay-mobile-devices
  3. (2015) Implement viewport-width-based fast-click heuristic
  4. (2016) 無線端瀏覽器 click 事件 300ms 延遲
  5. (2016) 300ms-tap-delay-gone-away
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末旨巷,一起剝皮案震驚了整個濱河市巨缘,隨后出現的幾起案子,更是在濱河造成了極大的恐慌采呐,老刑警劉巖若锁,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異斧吐,居然都是意外死亡又固,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門煤率,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仰冠,“玉大人,你說我怎么就攤上這事蝶糯⊙笾唬” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵昼捍,是天一觀的道長识虚。 經常有香客問我,道長端三,這世上最難降的妖魔是什么舷礼? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮郊闯,結果婚禮上妻献,老公的妹妹穿的比我還像新娘蛛株。我一直安慰自己,他們只是感情好育拨,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布谨履。 她就那樣靜靜地躺著,像睡著了一般熬丧。 火紅的嫁衣襯著肌膚如雪笋粟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天析蝴,我揣著相機與錄音害捕,去河邊找鬼。 笑死闷畸,一個胖子當著我的面吹牛尝盼,可吹牛的內容都是我干的。 我是一名探鬼主播佑菩,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼盾沫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了殿漠?” 一聲冷哼從身側響起赴精,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绞幌,沒想到半個月后蕾哟,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡莲蜘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年渐苏,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菇夸。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仪吧,靈堂內的尸體忽然破棺而出庄新,到底是詐尸還是另有隱情,我是刑警寧澤薯鼠,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布择诈,位于F島的核電站,受9級特大地震影響出皇,放射性物質發(fā)生泄漏羞芍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一郊艘、第九天 我趴在偏房一處隱蔽的房頂上張望荷科。 院中可真熱鬧唯咬,春花似錦、人聲如沸畏浆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刻获。三九已至蜀涨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝎毡,已是汗流浹背厚柳。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沐兵,地道東北人别垮。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像痒筒,于是被迫代替她去往敵國和親宰闰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內容