前端穿透問題

2022-08-05 07:39:17 字數 3570 閱讀 6235

同時自己也學習下,做乙個備忘

做過移動端h5頁面的同學肯定知道,移動端web的事件模型不同於pc頁面的事件。看了一些關於touch事件的文章,我想再來回顧下touch事件的原理,為什麼通過touch可以觸發click事件,touch事件是不是萬能的以及它可能存在的問題。

pc網頁上的大部分操作都是用滑鼠的,即響應的是滑鼠事件,包括mousedownmouseupmousemoveclick事件。一次點選行為,事件的觸發過程為:mousedown->mouseup->click三步。

手機上沒有滑鼠,所以就用觸控事件去實現類似的功能。touch事件包含touchstarttouchmovetouchend,注意手機上並沒有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中的屬性,它有很多取值,有用的主要是autonone,其他屬性值為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生成乙個指定字首的位址,讓測試人員無...