移動端的問題
移動端的主要問題是click會有300ms的延遲,主要原因是蘋果手機在設計時,考慮到用戶在瀏覽網頁時需要放大,所以,在用戶點擊的300ms之后,才觸發click,如果300ms之內還有click,就會進行放大縮小。
但是,問題是大部分時候放大、縮小時不需要的,有時開發者也會禁用他們,那么300ms的延遲就是性能上的損耗的,所以,如何解決這300ms的延遲? 在移動端,最容易想到的就是使用touchend來替代click,但是touchend是存在很大的問題的,因為touchend之前可能是touchstart、touchmove,最后才是touchstart,具體情境可能是用戶滑動頁面時,不小心在一個按鈕那里觸發了touchend,這樣就執行了,但是用戶的本意不是如此。 那么該怎么解決呢?
tap事件
為了減少這300ms的延遲,tap事件被很多框架(如zepto)封裝,來減少這延遲問題, tap事件不是原生的,所以是封裝的,那么具體是如何實現的呢?
主要考慮到下面兩點:
- 按住的事件不能超過延時時間,因為長時間可能就是瀏覽器的復制、粘貼等操作了。
- 不能在頁面中移動,移動是不能觸發tap事件的。
我們可以封裝如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>tap</title> <meta name="viewport" content="width=device-width,initial-scale=1"> </head> <body> <button id="btn">按鈕</button> <script> function tap(ele, callback) { // 記錄開始時間 var startTime = 0, // 控制允許延遲的時間 delayTime = 200, // 記錄是否移動,如果移動,則不觸發tap事件 isMove = false; // 在touchstart時記錄開始的時間 ele.addEventListener('touchstart', function (e) { startTime = Date.now(); }); // 如果touchmove事件被觸發,則isMove為true ele.addEventListener('touchmove', function (e) { isMove = true; }); // 如果touchmove事件觸發或者中間時間超過了延遲時間,則返回,否則,調用回調函數。 ele.addEventListener('touchend', function (e) { if (isMove || (Date.now() - startTime > delayTime)) { return; } else { callback(e); } }) } var btn = document.getElementById('btn'); tap(btn, function () { alert('taped'); }); </script> </body> </html>
如上,我們就可以正常使用tap事件並且避免了300ms延遲的產生。
點透問題
如果我們在移動端所有的click都替換為了tap事件,還是會觸發點透問題的,因為實質是: 在同一個z軸上,z-index不同的兩個元素,上面的元素是一個綁定了tap事件的,下面是一個a標簽,一旦tap觸發,這個元素就會display: none,而從上面的tap可以看出,有touchstart、touchend,所以會300ms之后觸發click事件,而z-index已經消失了,所以,觸發了下面的a的click事件,注意: 我們認為a標簽默認是綁定了click事件的。而這種現象不是我們所期待的。
解決方案: (1)使用fastclick。 (2)添加一個延遲。
(1)直接引入fastclick庫。
window.addEventListener("load", function () { FastClick.attach(document.body); }, false);
這樣,就可以成功解決問題了。
(2)對於上一個tap做延遲。
tap(ele, function () { setTimeout(function () { ele.style.display = 'none'; }, 300); })
這樣,過了300ms,那么click事件就不會觸發在下面的a標簽上了。
一個人可以過孤獨的生活,但一個人不可以過沒有向往的生活。 --- 俞敏洪
