移動端為何不使用click而模擬tap事件及解決方案


移動端click會遇到2個問題,click會有200-300ms的延遲,同時click事件的延遲響應,會出現穿透,即點擊會觸發非當前層的點擊事件。

 

為什么會存在延遲?

Google開發者文檔中有提到:

mobile browsers will wait approximately 300ms from the time that you tap the button to fire the click event. The reason for this is that the browser is waiting to see if you are actually performing a double tap.

      移動瀏覽器為什么會設置300毫秒的等待時間呢?這與雙擊縮放的方案有關。平時我們有可能已經注意到了,雙擊縮放,即用手指在屏幕上快速點擊兩次,可以看到內容或者圖片放大,再次雙擊,瀏覽器會將網頁縮放至原始比例。

      瀏覽器捕獲第一次單擊后,會先等待一段時間,如果在這段時間區間里用戶未進行下一次點擊,則瀏覽器會做單擊事件的處理。如果這段時間里用戶進行了第二次單擊操作,則瀏覽器會做雙擊事件處理。這段時間就是上面提到的300毫秒延遲。

 

touch事件的來源

PC網頁上的大部分操作都是用鼠標的,即響應的是鼠標事件,包括mousedown、mouseup、mousemove和click事件。一次點擊行為,事件的觸發過程為:mousedown -> mouseup -> click 三步。

手機上沒有鼠標,所以就用觸摸事件去實現類似的功能。touch事件包含touchstart、touchmove、touchend,注意手機上並沒有tap事件。手指觸發觸摸事件的過程為:touchstart -> touchmove -> touchend。

手機上沒有鼠標,但不代表手機不能響應mouse事件(其實是借助touch去觸發mouse事件)。有人在PC和手機上對事件做了對比實驗,以說明手機對touch事件相應速度快於mouse事件

 

點擊穿透問題

點擊穿透現象主要有3種:

1點擊穿透問題:點擊蒙層(mask)上的關閉按鈕,蒙層消失后發現觸發了按鈕下面元素的click事件,蒙層的關閉按鈕綁定的是touch事件,而按鈕下面元素綁定的是click事件,touch事件觸發之后,蒙層消失了,300ms后這個點的click事件fire,event的target自然就是按鈕下面的元素,因為按鈕跟蒙層一起消失了

2跨頁面點擊穿透問題:如果按鈕下面恰好是一個有href屬性的a標簽,那么頁面就會發生跳轉,因為a標簽跳轉默認是click事件觸發,所以原理和上面的完全相同

3另一種跨頁面點擊穿透問題:這次沒有mask了,直接點擊頁內按鈕跳轉至新頁,然后發現新頁面中對應位置元素的click事件被觸發了,和蒙層的道理一樣,js控制頁面跳轉的邏輯如果是綁定在touch事件上的,而且新頁面中對應位置的元素綁定的是click事件,而且頁面在300ms內完成了跳轉,三個條件同時滿足,就出現這種情況了

4非要細分的話還有第四種,不過概率很低,就是新頁面中對應位置元素恰好是a標簽,然后就發生連續跳轉了。。。諸如此類的,都是點擊穿透問題

 

為什么會出現點透

 click延遲,延遲,還是延遲。

在移動端不使用click而用touch事件代替觸摸是因為click事件有着明顯的延遲,具體touchstart與click的區別如下:

1.touchstart:在這個DOM(或冒泡到這個DOM)上手指觸摸開始即能立即觸發

2.click:在這個DOM(或冒泡到這個DOM)上手指觸摸開始,且手指未曾在屏幕上移動(某些瀏覽器允許移動一個非常小的位移值),且在這個在這個dom上手指離開屏幕,且觸摸和離開屏幕之間的間隔時間較短(某些瀏覽器不檢測間隔時間,也會觸發click)才能觸發。

也就是說,事件的觸發時間按由早到晚排列為:touchstart 早於 touchend 早於 click。亦即click的觸發是有延遲的,這個時間大概在300ms左右。

由於我們在touchstart階段就已經隱藏了罩層A,當click被觸發時候,能夠被點擊的元素則是其下的B元素,根據click事件的觸發規則:只有在被觸發時,當前有click事件的元素顯示,且在面朝用戶的最前端時,才觸發click事件。

由於B綁定了click事件(或者B本身默認存在click事件),所以B的click事件被觸發,產生了點透的情況。

 

點透的解決方案

1. 使用fastclick庫,其實現思路是,取消 click 事件(參看源碼 164-173 行),用 touchend 模擬快速點擊行為(參看源碼 521-610 行)。

FastClick.attach(document.body);

從此所有點擊事件都使用click,不會出現“穿透”的問題,並且沒有300ms的延遲。

然后給需要“無延遲點擊”的元素綁定click事件(注意不再是綁定zepto的tap事件)即可。

也可以不在body上初始化它,而在某個dom上初始化,這樣,只有設個dom和它的子元素才能享受"無延遲"的點擊

實踐開發中,當元素綁定fastclick后,click響應速度比tap還要快一點。

2.為元素綁定touchend事件,並在內部加上e.preventDefault();

$demo.on('touchend',function(e){

//改變了事件名稱,tap是body上才被觸發,而touchend是原生的事件,在dom本身上就會被捕獲觸發

$demo.hide();

e.preventDefault();//阻止“默認行為”

});

 3. 遮擋

由於 click 事件的滯后性,在這段時間內原來點擊的元素消失了,於是便“穿透”了。因此我們順着這個思路就想到,可以給元素的消失做一個fade效果,類似jQuery里的fadeOut,並設置動畫duration大於300ms,這樣當延遲的 click 觸發時,就不會“穿透”到下方的元素了。

同樣的道理,不用延時動畫,我們還可以動態地在觸摸位置生成一個透明的元素,這樣當上層元素消失而延遲的click來到時,它點擊到的是那個透明的元素,也不會“穿透”到底下。在一定的timeout后再將生成的透明元素移除。

4.pointer-events

pointer-events是CSS3中的屬性,它有很多取值,有用的主要是auto和none,其他屬性值為SVG服務。

auto:    效果和沒有定義 pointer-events 屬性相同,鼠標不會穿透當前層。

none:   元素不再是鼠標事件的目標,鼠標不再監聽當前層而去監聽下面的層中的元素。但是如果它的子元素設置了pointer-events為其它值,比如auto,鼠標還是會監聽這個子元素的。

5.只用touch

最簡單的解決方案,完美解決點擊穿透問題

把頁面內所有click全部換成touch事件( touchstart 、’touchend’、’tap’),需要特別注意 a標簽,a標簽的href也是click,需要去掉換成js控制的跳轉,或者直接改成span + tap控制跳轉。如果要求不高,不在乎滑走或者滑進來觸發事件的話,span + touchend就可以了,畢竟tap需要引入第三方庫

不用a標簽其實沒什么,移動app開發不用考慮SEO,即便用了a標簽,一般也會去掉所有默認樣式,不如直接用span

 

tap事件的原理:

tap事件的原理其實是源於觸摸touch事件,在移動觸摸事件就是在同個點觸發,及touchmove的距離距離touchstar的距離為0,並且點擊的時間不超過某個設定的時間值,超過該時間值的話,就屬於長按了

下面我封裝了一個事件,模擬tap事件的原理:

封裝tap的方法

    function tap(ele,callBack){

        //觸摸開始的時間

        var startTime=0;

        //定義touchmove是否觸發

        var ismove=false;

        var maxTime=250;

        ele.addEventListener('touchstart',function(e){

            startTime=Date.now();

            ismove=false;

        })

        ele.addEventListener('touchmove',function(e){

            //觸發就賦值為true

            ismove=true;

        })

        ele.addEventListener('touchend',function(e){

            //判斷是否是touchmove是否觸發

            if (ismove) {

                return;

            }

            // 判斷是否為長按

            if ((Date.now()-startTime)>maxTime) {

                return;

            }

 

        // 如果能夠到這里

        callBack(e);

        })

   

    }

 

移動端模擬雙擊事件 

var  clicked = 1;

var  clickedTime={

        'timeA':" ",

       'timeB':" "

 };

var clicking=function()

{

           if(clicked==1){

              clickedTime.timeA=new Date();

              clicked++;             

          }

          else if(clicked==2){

            clickedTime.timeB=new Date();

            if (Math.abs(clickedTime.timeA-clickedTime.timeB)<400){

               //  雙擊 function

                clicked=1;

            }else{

              clickedTime.timeA=new Date();

            };

          }

      });

 

移動端  單擊事件 與 雙擊事件 共存 如(單擊 則function a() 雙擊function B())

var  clicked = 1;

var  clickedTime={

        'timeA':" ",

       'timeB':" "

 };

var clicking=function()

{

          if(clicked==1){

             // 單擊 function

            clicked++; 

          }

          else if(clicked==2){

              clickedTime.timeA=new Date();

              clicked++;             

          }

          else if(clicked==3){

            clickedTime.timeB=new Date();

            if (Math.abs(clickedTime.timeA-clickedTime.timeB)<400){

               //  雙擊 function

                clicked=1;

            }else{

              clickedTime.timeA=new Date();

            };

          }

      });

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM