描述
在近期的一個移動端項目中,有一個頁面需要有彈框提示,並且這個彈框通過關閉按鈕關閉。頁面當中使用了 iScroll 來實現頁面局部滾動,在 iScroll 的配置當中把 tap
和 click
事件都開啓了。
代碼如下:
this.myScroll = new IScroll(this.$refs.wrapper, { mouseWheel: true, click: true, tap: true})
在實現過程中,遇到了一個奇怪的問題,由於按鈕的位置與彈框右上角的關閉按鈕位置一致,當我點擊按鈕時,彈框一閃而過。
效果如下:
原因
什麼是點擊穿透?
假如頁面上有兩個元素A和B。B元素在A元素之上。我們在B元素的touchstart事件上註冊了一個回調函數,該回調函數的作用是隱藏B元素。我們發現,當我們點擊B元素,B元素被隱藏了,隨後,A元素觸發了click事件。
通過上網查找有關資料,翻閱了移動端的書籍,發現在手機端中,事件的觸發順序爲:touchstart
-> touchmove
-> touchend
,而 click
事件有 300ms 的延遲,當 touchstart
事件把B元素隱藏之後,隔了300ms,瀏覽器觸發了 click
事件,但是此時B元素不見了,所以該事件被派發到了A元素身上。如果A元素是一個鏈接,那此時頁面就會意外地跳轉。
解決方案
1. 改用 touch
事件
由於項目使用的是 Vue.js
,這裏就提供一下 Vue.js
的解決方法。使用了 vue-tap 的一個插件,具體使用方法參看官方文檔,在需要點擊事件的時候,通過 v-tap
指令來綁定。
// main.jsimport vueTap from 'v-tap' // 引入插件Vue.use(vueTap) // 全局註冊
v-tap="{methods:showReceiveModel}" // 在元素上綁定事件
2. 使用 fastclick 插件
這個也是在網上看到的,也可以解決點透問題,使用方法可以看 fastclick 的文檔,在這裏提供一下 Vue.js
的引入及使用
import FastClick from 'fastclick'; // 引入插件FastClick.attach(document.body, options); // 使用 fastclick
最終沒有使用這個方案是因爲有一些小 bug ,如 Fastclick 導致click事件觸發兩次的問題。
其他
tap
一詞
對於 tap
這個詞,用過 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
。
參考
博文
書籍
《移動 Web 手冊》