設計方案--移動端延遲300ms的原因以及解決方案


一、前言

移動端瀏覽器提供一個特殊的功能:雙擊(double tap)縮放。

 

二、移動端延遲300ms的原因

為什么要用觸摸事件?觸摸事件是移動端瀏覽器特有的html5事件。

因為移動端的click有很大延遲(大約300ms),300ms延遲來自判斷雙擊和長按,因為只有默認等待時間結束以確定沒有后續動作發生時,才會觸發click事件。而觸摸事件的延遲則是非常短的,使用觸摸事件的能夠提高頁面響應速度,帶來更好的用戶體驗。

重點:由於移動端會有雙擊縮放的這個操作,因此瀏覽器在click之后要等待300ms,看用戶有沒有下一次點擊,也就是這次操作是不是雙擊。

 

三、瀏覽器開發商的解決方案

1、方案一:禁用縮放

當HTML文檔頭部包含如下meta標簽時:

<meta name="viewport" content="user-scalable=no"> <meta name="viewport" content="initial-scale=1,maximum-scale=1">

表明這個頁面是不可縮放的,那雙擊縮放的功能就沒有意義了,此時瀏覽器可以禁用默認的雙擊縮放行為並且去掉300ms的點擊延遲

缺點:就是必須通過完全禁用縮放來達到去掉點擊延遲的目的,然而完全禁用縮放並不是我們的初衷,我們只是想禁掉默認的雙擊縮放行為,這樣就不用等待300ms來判斷當前操作是否是雙擊。但是通常情況下,我們還是希望頁面能通過雙指縮放來進行縮放操作,比如放大一張圖片,放大一段很小的文字。

 

2、方案二:更改默認的視口窗口

為了讓桌面站點能在移動端瀏覽器正常顯示,移動端瀏覽器默認的視口寬度!=設備瀏覽器視窗寬度,而是視口寬度要比設備寬度大,通常是980px。

我們可以通過以下標簽來設置視口寬度設備寬度

<meta name="viewport" content="width=device-width">
 

對移動端坐過適配和優化了,這個時候就不需要雙擊縮放了。如果能夠識別出一個網站是響應式的網站,那么移動端瀏覽器就可以自動禁掉默認的雙擊縮放行為並且去掉300ms的點擊延遲。如果設置了上述meta標簽,那瀏覽器就可以認為該網站已經對移動端做過了適配和優化,就無需雙擊縮放操作了。

這個方案相比方案一的好處在於,它沒有完全禁用縮放,而只是禁用了瀏覽器默認的雙擊縮放行為,但用戶仍然可以通過雙指縮放操作來縮放頁面

 

方案三:css 的 touch-action

除了IE之外的大部分瀏覽器都不支持這個新的CSS屬性。touch-action這個CSS屬性。這個屬性指定了相應元素上能夠觸發的用戶代理(也就是瀏覽器)的默認行為。如果將該屬性值設置為touch-action: none,那么表示在該元素上的操作不會觸發用戶代理的任何默認行為,就無需進行300ms的延遲判斷

 

四、代碼解決方案

1、方案一:指針事件polyfill

除了IE,其他大部分瀏覽器都還不支持指針事件。有一些JS庫,可以讓我們提前使用指針事件。比如:

(1)谷歌的Polymer

(2)微軟的HandJS

(3)@Rich-Harris 的 Points

關心的不是指針事件,而是與300ms延遲相關的CSS屬性touch-action。由於除了IE之外的大部分瀏覽器都不支持這個新的CSS屬性,所以這些指針事件的polyfill必須通過某種方式去模擬支持這個屬性。一種方案是JS去請求解析所有的樣式表,另一種方案是將touch-action作為html標簽的屬性。

 

2、方案二:FastClick

FastClickFT Labs專門為解決移動端瀏覽器 300 毫秒點擊延遲問題所開發的一個輕量級的庫。FastClick的實現原理是在檢測到touchend事件的時候,會通過DOM自定義事件立即出發模擬一個click事件,並把瀏覽器在300ms之后的click事件阻止掉。

 

五、點擊穿透問題

說完移動端點擊300ms延遲的問題,還不得不提一下移動端點擊穿透的問題。既然click點擊有300ms的延遲,那對於觸摸屏,我們直接監聽touchstart事件不就好了嗎?

使用touchstart去代替click事件有兩個不好的地方。

第一:touchstart是手指觸摸屏幕就觸發,有時候用戶只是想滑動屏幕,卻觸發了touchstart事件,這不是我們想要的結果;

第二:使用touchstart事件在某些場景下可能會出現點擊穿透的現象。

1、什么是點擊穿透?

假如頁面上有兩個元素A和B。B元素在A元素之上。我們在B元素的touchstart事件上注冊了一個回調函數,該回調函數的作用是隱藏B元素。我們發現,當我們點擊B元素,B元素被隱藏了,隨后,A元素觸發了click事件。

這是因為在移動端瀏覽器事件執行的順序是touchstart > touchend > click。而click事件有300ms的延遲,當touchstart事件把B元素隱藏之后,隔了300ms,瀏覽器觸發了click事件,但是此時B元素不見了,所以該事件被派發到了A元素身上。如果A元素是一個鏈接,那此時頁面就會意外地跳轉。

 

2、點擊穿透現象3種情況

(1)點擊穿透問題:點擊蒙層(mask)上的關閉按鈕,蒙層消失后發現觸發了按鈕下面元素的click事件。

(2)跨頁面點擊穿透問題:如果按鈕下面恰好是一個有href屬性的a標簽,那么頁面就會發生跳轉因為 a標簽跳轉默認是click事件觸發 ,所以原理和上面的完全相同

(3)點擊穿透問題:這次沒有mask了,直接點擊頁內按鈕跳轉至新頁,然后發現新頁面中對應位置元素的click事件被觸發了。

 

3、解決方案

2種思路:

(1)不要混用touch和click。既然touch之后300ms會觸發click,只用touch或者只用click就自然不會存在問題了。

(2)用掉(或者說是消費掉)touch之后的click。依舊用tap,只是在可能發生點擊穿透的情形做額外的處理,拿個東西來擋住、或者tap后延遲350毫秒再隱藏mask、pointer-events、在下面元素的事件處理器里做檢測(配合全局flag)

詳細方案:

(1)只用touch

最簡單的解決方案,完美解決點擊穿透問題。

把頁面內所有click全部換成touch事件 touchstart 、’touchend’、’tap’, 需要特別注意 a標簽,a標簽的href也是click,需要去掉換成js控制的跳轉,或者直接改成span + tap控制跳轉。

(2)只用click

下下策 ,因為會帶來300ms延遲,頁面內任何一個自定義交互都將增加300毫秒延遲,想想都慢。不用touch就不會存在touch之后300ms觸發click的問題。

(3)拿個東西擋住

比較笨的方法, 千萬不要用。更多信息請查看 【移動端兼容問題研究】javascript事件機制詳解(涉及移動兼容)

(4)tap后延遲350ms再隱藏mask

改動最小,缺點是隱藏mask變慢了,350ms還是能感覺到慢的。

(5)pointer-events

比較麻煩且有缺陷, 不建議使用。mask隱藏后,給按鈕下面元素添上 pointer-events: none; 樣式,讓click穿過去,350ms后去掉這個樣式,恢復響應。缺陷是mask消失后的的350ms內,用戶可以看到按鈕下面的元素點着沒反應,如果用戶手速很快的話一定會發現。

(6)在下面元素的事件處理器里做檢測(配合全局flag)

比較麻煩, 不建議使用。全局flag記錄按鈕點擊的位置(坐標點),在下面元素的事件處理器里判斷event的坐標點,如果相同則是那個可惡的click,拒絕響應。

(7)fastclick

好用的解決方案,不介意多加載幾KB的話, 不建議使用 ,因為有人遇到了bug,更多信息請查看: Fastclick 導致click事件觸發兩次的問題

 

首先引入fastclick庫,再把頁面內所有touch事件都換成click,其實稍微有點麻煩,建議引入這幾KB就為了解決點透問題不值得,不如用第一種方法呢。

 

六、瀏覽器事件觸發的順序

touchstart --> mouseover(有的瀏覽器沒有實現) --> mousemove(一次) -->mousedown --> mouseup --> click -->touchend

Touch 事件中,常用的為 touchstart, touchmove, touchend 三種。除此之外還有touchcancel。 注意,原生事件中並沒有tap事件。

事件描述如下:

事件 描述 觸發時機
touchstart 開始觸摸 手指接觸屏幕時立即觸發
touchmove 移動或拖拽 取決於系統和瀏覽器
touchend 觸摸結束 手指離開屏幕時立即出發

而Touch事件的觸發一般通過手指,還會存在多點觸控,拖拽方向等情況。列出幾個重要參數如下:

參數 含義
touches 屏幕中每根手指信息列表
targetTouches 和touches類似,把同一節點的手指信息過濾掉
changedTouches 響應當前事件的每根手指的信息列表

代碼獲取如下:

elemenrRef.addEventListener('touchstart', function(e) { console.log(e.touches, e.targetTouches, e.changedTouches);} );

 

手指觸發觸摸事件的過程如下:

touchstart --> mouseover(有的瀏覽器沒有實現) --> mousemove(一次) -->mousedown --> mouseup --> click -->touchend

由此,我們可以在 ontouchstart 事件上記錄開始觸摸開始,ontouchend 記錄觸摸結束信息。 通過上述這些參數,很容易的去計算幽冥點擊的時間,以及點擊穿透的相關信息,包括響應的坐標情況

 

【注:我是saucxs,也叫songEagle,松寶寫代碼,文章首發於sau交流學習社區 https://www.mwcxs.top),關注我們每天閱讀更多精彩內容】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM