移動端的touch click事件的理解+點透


移動端在touch上一共有4個事件

touchstart touchmove touchend touchcancel, touchcancel, 一般來說,它們執行的順序為 touchstart -> touchmove -> touchend -> touchcancel . 其中touchcancel一般情況下不會觸發,也不是這里討論的焦點;

這里會結合click對上面的事件進行討論, touch發生在click之前

先上段代碼,直觀感受一下

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      #level0 {
        /* width: 500px;
        height: 500px; */
      }

      #level1-0 {
        background: red;
        width: 500px;
        height: 500px;
      }

      #level1-1 {
        background: green;
        width: 500px;
        height: 500px;
      }
    </style>
  </head>
  <body>
    <div id="level0">
      <div id="level1-0">
      </div>
      <div id="level1-1">
      </div> 
    </div>
  </body>
  <script type="text/javascript">

    var level10 = document.getElementById("level1-0");

    level10.addEventListener('touchstart', function(e) {
      console.log(1);
    });

    level10.addEventListener('touchmove', function(e) {
      console.log(2);
    });

    level10.addEventListener('touchend', function(e) {
      console.log(3);
    });

    level10.onclick = function() {
      console.log(5);
    }

    document.body.onclick = function() {
      console.log('6');
    }

  </script>
</html>
在紅色區域點擊會出現什么效果呢? 出現的是 1 3 5 6, 奇怪了 touchmove 為何不執行,因為我們並沒有移動,也就是說,必須觸碰到屏幕上面,而且發生了移動動作,touchmove才執行,現在我們觸碰到,而且手指稍微動一下,發現輸出的效果是, 1 2(+) 3, 其中touchmove 可能觸發多次,又奇怪了, click為何不執行, 因為 click執行的條件是 點擊, 而且不移動 所以一般情況下,我們可以理解成 touchmove和click是相斥的。
我們知道,當一個用戶在點擊屏幕的時候,系統會觸發touch事件和click事件,touch事件優先處理,touch事件經過 捕獲,處理, 冒泡 一系列流程處理完成后, 才回去觸發click事件
既然touch事件和click事件有了優先級別,那么能不能在touch階段取消掉系統觸發的click事件呢?當然是可以的,瀏覽器提供了這樣的能力。在touch事件里面,調用e.preventDefault() 就可以阻止本次點擊系統觸發的click事件,即本次相關的click都不會執行
 
把上面代碼稍微加一點
level10.addEventListener('touchstart', function(e) {
      console.log(1);
      e.preventDefault();
});

點擊的時候 發現 只有 1 3, 說明click被阻止了,當然在touchend里面加效果也一樣,所以 在touch事件里面加 e.preventDefault可以取消系統產生的click事件, 當然不會阻止后面的touch事件。

用個具體的例子看看 如何解決點透問題

產生點透問題的原因, 可以先看看代碼吧

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      #level0 {
        /* width: 500px;
        height: 500px; */
        position: relative;
      }

      #level1-0 {
        position: absolute;
        z-index: 1;
        background: red;
        width: 500px;
        height: 500px;
      }

      #level1-1 {
        background: green;
        width: 500px;
        height: 500px;
      }
    </style>
  </head>
  <body>
    <div id="level0">
      <div id="level1-0">
      </div>
      <div id="level1-1">
      </div> 
    </div>
  </body>
  <script type="text/javascript">

    var level10 = document.getElementById("level1-0");
    var level11 = document.getElementById("level1-1");


    level10.addEventListener('touchstart', function(e) {
      level10.style.display = 'none';
    });

    level11.onclick = function() {
      console.log('level11莫名被點擊了');
    }

  </script
</html>
本來是 level1-0 和 level1-1是兄弟節點,即他們之間不會發生什么 事件傳遞, 目前level1-0相當於一個覆蓋層,覆蓋在level1-1上面, 按理說點擊 level1-0的時候,level1-0會阻擋所有的事件,事件不會傳遞給level1-1,當點擊level1-0的時候,實際上level1-1也發生了點擊事件,即上面的輸出結果為level1-0消失, 輸出 level11莫名被點擊了, 這就是點透
點透發生的條件:
  1. A 和 B不是后代繼承關系(如果是后代繼承關系的話,就直接是冒泡子類的話題了)
  2. A發生touch, A touch后立即消失, B事件綁定click
  3. A z-index大於B,即A顯示在B浮層之上
點透發生的理由: 當手指觸摸到屏幕的時候,系統生成兩個事件,一個是touch 一個是click,touch先執行,touch執行完成后,A從文檔樹上面消失了,而且由於移動端click還有延遲200-300ms的關系,當系統要觸發click的時候,發現在用戶點擊的位置上面,目前離用戶最近的元素是B,所以就直接把click事件作用在B元素上面了.
那如何才能解決點透問題呢? 還記得我之前說過么,系統提供了先觸發的touch事件去取消系統生成的click事件,所以只要在touch事件的某個處理函數中 執行 e.preverDefault即可, 一般我們在touchend中執行
在上面代碼中,加上這句就完美解決了
level10.addEventListener('touchend', function(e) {
    e.preventDefault();
});

當然點透問題,還有其他的解決方法,關鍵是 要么是需求本次系統生成的click事件,要么是當系統觸發click的時候,當前的觸發touch的那個dom節點還存在。比如將其一延遲3s在關閉

setTimeout(() => {
        level10.style.display = 'none';
}, 300);

 


免責聲明!

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



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