一、前言
移動端浏覽器提供一個特殊的功能:輕按兩下(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
FastClick是FT 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);}
);
手指觸發觸摸事件的過程如下:
由此,我們可以在 ontouchstart 事件上記錄開始觸摸開始,ontouchend 記錄觸摸結束資訊。 通過上述這些參數,很容易的去計算幽冥點選的時間,以及點選穿透的相關資訊,包括響應的坐标情況。
【注:我是saucxs,也叫songEagle,松寶寫代碼,文章首發于sau交流學習社群 https://www.mwcxs.top),關注我們每天閱讀更多精彩内容】
文中有錯誤的地方希望指出,共同進步