最近一直在忙於一個無線端的項目,由於之前主要工作都是在桌面端,移動端接觸的比較少,所以中間遇到了很多的坑,做一個簡單的記錄。
問題背景
需求中有這樣的一個功能,點擊取件信息的時候會彈出一個地址列表的浮層,用戶選擇地址之后會將具體的地址回填到取件信息當中去。按道理講,這是一個非常簡單的功能,可是在開發過程中卻遇到了很多非常詭異的事情。
初始的代碼是這么寫的:
var pickupInfo = $("#pickupInfo");
pickupInfo.on("tap", function () {
var addressList = $("#addressList");
addressList.show();
});
window.addEventListener("message", function () {
// 回填地址信息
});
// addressList 是一個浮層
$("#addressList li").on("click", function () {
// 此處回填地址到取件信息中去
// 具體方式則是通過iframe的postMessage發送消息,pickupInfo那邊接收消息后填充
});
為了更清楚的說明問題,此處刪除了很多的業務邏輯。
在測試過程中就發現了一個詭異的問題,在點擊pickupInfo的時候,addressList浮層閃了一下就消失了,彈出addressList浮層后立即回填了地址信息。
因為一開始也沒有太注意觀察,跟代碼的過程中發現只要浮層一彈出,pickupInfo那邊就立即收到了message事件信息。當時第一反應感覺是地址列表那邊出了問題,看了好幾遍代碼,一直沒發現哪里有問題,也跟了幾次,每次都是地址列表一彈出就立即觸發了click事件。
奇了個怪了!
點擊穿透
試着描述了下問題google了一把,可能是描述的不太准確,啥也沒搜着。沒辦法了,瞪大眼睛再操作一遍。這時候奇跡出現了,我發現每次點擊pickupInfo,都在選中addressList中相對於屏幕與pickupInfo相同位置的地址。
查看了下事發地點的代碼,然后在google中敲下了這幾個字“移動端 tap”,一大堆的結果,看了幾篇文章,終於找到了問題的答案。
先說說touch事件吧。
我們知道,PC上有鼠標事件,一次點擊可以拆分成mousedown > click > mouseup 三步。移動端沒有鼠標,但是有類似的觸摸事件,用戶的一次點擊可以分為touchstart > touchmove > touchend。 雖然手機上沒有mouse事件,但是手機依然可以響應mouse事件,為什么呢?是通過touch事件來模擬的。有人曾經對比測試過手機上和PC上的mouse事件,發現手機上的mouse要慢一些,大概在300m左右。
再說說tap事件。
在PC端,我們經常使用到click事件,對應到移動端,我們使用tap事件。但原生的touch事件本身是沒有tap的,也是通過模擬產生的。在Zepto中,如果在touchend事件響應250ms無操作后,會觸發singleTap事件。
// trigger single tap after 250ms of inactivity
else {
touchTimeout = setTimeout(function(){
touchTimeout = null
if (touch.el) touch.el.trigger('singleTap')
touch = {}
}, 250)
}
至此,點擊穿透的原因就明了了。
當pickupInfo監聽的tap事件得到響應之后,會立即彈出addressList浮層,此時瀏覽器還會觸發click事件,而此時原來的pickupInfo已經完全被addressList遮罩,所以click事件就被觸發在了與pickupInfo相同位置的addressList中對應的區域,也就正好響應了addressList中監聽的click事件。
世事奈何如此之巧~
解決方案
問題清楚了,修改方法也就明確了,由於項目還在開發中,個人覺得當前的方案還不是特別好,后續優化后再貼出來。這里貼篇文章,也來說說touch事件與點擊穿透問題,里面對touch和tap事件做了詳細的說明,對點擊穿透問題也提到了幾個方案,可以參考。
小結
這兩天一直在忙於趕開發進度,對於過程中遇到的一些典型的問題做一些記錄,算是一種沉淀吧~~