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

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

touchstart touchmove touchend touchcancel, touchcancel, 通常來講,它們執行的順序爲 touchstart -> touchmove -> touchend -> touchcancel . 其中touchcancel通常狀況下不會觸發,也不是這裏討論的焦點;

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

先上段代碼,直觀感覺一下html

<!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事件。java

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

產生點透問題的緣由, 能夠先看看代碼吧瀏覽器

<!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在關閉dom

setTimeout(() => {
        level10.style.display = 'none';
}, 300);
相關文章
相關標籤/搜索