同時自己也學習下,做乙個備忘
做過移動端h5頁面的同學肯定知道,移動端web的事件模型不同於pc頁面的事件。看了一些關於touch事件的文章,我想再來回顧下touch事件的原理,為什麼通過touch可以觸發click事件,touch事件是不是萬能的以及它可能存在的問題。
pc網頁上的大部分操作都是用滑鼠的,即響應的是滑鼠事件,包括mousedown
、mouseup
、mousemove
和click
事件。一次點選行為,事件的觸發過程為:mousedown
->mouseup
->click
三步。
手機上沒有滑鼠,所以就用觸控事件去實現類似的功能。touch事件包含touchstart
、touchmove
、touchend
,注意手機上並沒有tap
事件。手指觸發觸控事件的過程為:touchstart
->touchmove
->touchend
。
手機上沒有滑鼠,但不代表手機不能響應mouse事件(其實是借助touch去觸發mouse事件)。有人在pc和手機上對事件做了對比實驗,以說明手機對touch事件相應速度快於mouse事件。
可以看到在手機上,當我們手觸碰螢幕時,要過300ms左右才會觸發mousedown
事件,所以click
事件在手機上看起來就像慢半拍一樣。
引數含義
touches
螢幕中每根手指資訊列表
targettouches
和touches類似,把同一節點的手指資訊過濾掉
changedtouches
響應當前事件的每根手指的資訊列表
用過zepto或kissy等移動端js庫的人肯定對tap
事件不陌生,我們做pc頁面時繫結click
,相應地手機頁面就繫結tap
。但原生的touch事件本身是沒有tap的,js庫里提供的tap事件都是模擬出來的。
我們在上面看到,手機上響應 click 事件會有300ms的延遲,那麼這300ms到底是幹嘛了?瀏覽器在 touchend 後會等待約300ms,原因是判斷使用者是否有雙擊(double tap)行為。如果沒有 tap 行為,則觸發 click 事件,而雙擊過程中就不適合觸發 click 事件了。由此可以看出 click 事件觸發代表一輪觸控事件的結束。
既然說tap事件是模擬出來的,我們可以看下zepto對 singletap 事件的處理。見原始碼 136-143 行,可以看出在 touchend 響應 250ms 無操作後,則觸發singletap。
整個容器裡有乙個底層元素的div,和乙個彈出層div,為了讓彈出層有模態框的效果,我又加了乙個遮罩層。
底層元素div>
彈出層div>
class="btn" id="closepopup">關閉
div>
div>
div>
div>
然後為底層元素繫結 click 事件,而彈出層的關閉按鈕繫結 tap 事件。
$('#closepopup').on('tap', function(e));
$('#underlayer').on('click', function());
點選關閉按鈕,touchend首先觸發tap,彈出層和遮罩就被隱藏了。touchend後繼續等待300ms發現沒有其他行為了,則繼續觸發click,由於這時彈出層已經消失,所以當前click事件的target就在底層元素上,於是就alert內容。整個事件觸發過程為 touchend -> tap -> click。
而由於click事件的滯後性(300ms),在這300ms內上層元素隱藏或消失了,下層同樣位置的dom元素觸發了click事件(如果是input框則會觸發focus事件),看起來就像點選的target「穿透」到下層去了。
zepto中的 tap 通過兼聽繫結在 document 上的 touch 事件來完成 tap 事件的模擬的,是通過事件冒泡實現的。在點選完成時(touchstart / touchend)的 tap 事件需要冒泡到 document 上才會觸發。而在冒泡到 document 之前,手指接觸和離開螢幕(touchstart / touchend)是會觸發 click 事件的。
因為 click 事件有延遲(大概是300ms,為了實現safari的雙擊事件的設計),所以在執行完 tap 事件之後,彈出層立馬就隱藏了,此時 click 事件還在延遲的 300ms 之中。當 300ms 到來的時候,click 到的其實是隱藏元素下方的元素。
如果正下方的元素有繫結 click 事件,此時便會觸發,如果沒有繫結 click 事件的話就當沒發生。如果正下方的是 input 輸入框(或是 select / radio / checkbox),點選預設 focus 而彈出輸入鍵盤,也就出現了上面的「點透」現象。
由於 click 事件的滯後性,在這段時間內原來點選的元素消失了,於是便「穿透」了。因此我們順著這個思路就想到,可以給元素的消失做乙個fade效果,類似jquery裡的fadeout
,並設定動畫duration大於300ms,這樣當延遲的 click 觸發時,就不會「穿透」到下方的元素了。
同樣的道理,不用延時動畫,我們還可以動態地在觸控位置生成乙個透明的元素,這樣當上層元素消失而延遲的click來到時,它點選到的是那個透明的元素,也不會「穿透」到底下。在一定的timeout後再將生成的透明元素移除。
//遮擋物var $tap = $('
'); $tap.css();
$close.on('touchend', function
(e));
settimeout(
function
(), 400);
});$under.on('click', function
());
pointer-events
是css3中的屬性,它有很多取值,有用的主要是auto
和none
,其他屬性值為svg服務。
取值含義
auto
效果和沒有定義 pointer-events 屬性相同,滑鼠不會穿透當前層。
none
元素不再是滑鼠事件的目標,滑鼠不再監聽當前層而去監聽下面的層中的元素。但是如果它的子元素設定了pointer-events為其它值,比如auto,滑鼠還是會監聽這個子元素的。
$('#closepopup').on('tap', function(e), 400);
});
使用fastclick庫,其實現思路是,取消 click 事件(參看原始碼 164-173 行),用 touchend 模擬快速點選行為(參看原始碼 521-610 行)。
fastclick.attach(document.body);
從此所有點選事件都使用click
,不會出現「穿透」的問題,並且沒有300ms的延遲。 前端內網穿透,localtunnel
我的理解是 將您的本地主機公開到世界各地,便於測試和共享,無需混淆dns或部署只是為了讓其他人測試您的更改。通過localtunnel就能把你的本地位址對映到乙個公共位址,讓其他使用者也能訪問,比如我本地位址localhost 8888,通過localtunnel生成乙個指定字首的位址,讓測試人員無...
滾動穿透問題
眾所周知,移動端有fixed遮罩或彈出層時,螢幕上滑動,後面的背景也會跟著滾動,強迫症的我總是覺得不舒服。然而也沒有找到完美的解決方法。這裡說說兩種能用的方法。1 遮罩或彈層沒有滾動的內容時 vue裡提供了乙個很好用的解決辦法 touchmove.prevent 如果不用vue的話,那就監聽touc...
前端內網穿透,localtunnel你值得擁有!
我的理解是 將您的本地主機公開到世界各地,便於測試和共享,無需混淆dns或部署只是為了讓其他人測試您的更改。通過localtunnel就能把你的本地位址對映到乙個公共位址,讓其他使用者也能訪問,比如我本地位址localhost 8888,通過localtunnel生成乙個指定字首的位址,讓測試人員無...