[ 筆記 ] DOM - 事件傳遞機制:捕獲與冒泡、事件代理


Posted by krebikshaw on 2020-07-17

事件傳遞機制

  • 捕獲: CAPTURING_PHASE
    • DOM 的事件在傳播時,會先從根節點開始往下傳遞到target,這邊你如果加上事件的話,就會處於CAPTURING_PHASE,捕獲階段。
  • 元素本身:AT_TARGET
    • target就是你所點擊的那個目標,這時候在target身上所加的eventListenr會是AT_TARGET這一個 Phase。
  • 冒泡:BUBBLING_PHASE
    • 最後,事件再往上從子節點一路逆向傳回去根節點,這時候就叫做BUBBLING_PHASE,也是冒泡階段。

  • 注意:掌握原則
    1. 先捕獲,再冒泡
    2. 當事件傳到 target 本身,沒有分捕獲跟冒泡

設定於哪個階段做監聽

  • .addEventListener() 加上第三個參數
    • true:捕獲
    • false :冒泡
    • 若不寫第三個參數,則預設為 false

取消事件傳遞

  • e.stopPropagation()
  • 加在哪個階段上,事件的傳遞就斷在哪裡,不會再把事件傳遞給「下一個節點」
    • 加在「捕獲階段」上
      • outer「觸發」捕獲事件,不再傳遞給下一個節點

  • 加在「冒泡階段」上
    • btn「觸發」冒泡事件,不再傳遞給下一個節點

停止後續 v.s 停止預設

  • e.stopPropagation 停止的是後續事件
  • e.stopImmediatePropagation() 讓其他同一層級的 listener 也不要被執行
  • e.preventDefault() 可以停止瀏覽器的預設行為
  • 如果我想要阻止頁面上所有的 click 事件( 包括 <a> 預設的動作 ),可以在 window 物件監聽捕獲階段,來阻止底下的所有元素
window.addEventListener('click', function (e) {
  e.preventDefault(); // 停止預設功能
  e.stopPropagation(); // 停止後續傳遞
}, true) // 指定為從捕獲階段開始監聽

// 底下的事件傳遞全都被阻止了
function addEvent(className) {
  document.querySelector(className)
    .addEventListener('click', function (e) {
      console.log(className, '捕獲', e.eventPhase);
    }, true)
  document.querySelector(className)
  .addEventListener('click', function (e) {
    console.log(className, '冒泡', e.eventPhase);
  }, false)
};

新手易混肴問題

利用迴圈幫全部按鈕加上事件監聽,希望利用迴圈的 i 值,在按鈕被點選的時候加上編號 i。

const btnGroup = document.querySelectorAll('.btn');
for (var i = 1; i <= btnGroup.length; i += 1) {
  btnGroup[i].addEventListener('click', function (e) {
    console.log(i);
  })
}

但是實際上當 click 事件觸發時,迴圈已經跑完了(i 已經跑到 btnGroup.length),所以編號會通通是最後一號。

解決方式:
利用屬性 attribute 來紀錄每一圈新增按鈕時候的 i 值。這樣迴圈跑完後雖然 i 值已經跑到 btnGroup.length了, 每個按鈕依然有自己的 attribute 可以抓

<button class="btn" data-value="1">1</button>
<button class="btn" data-value="2">2</button>
<script>
  const btnGroup = document.querySelectorAll('.btn');
  for (let i = 0; i < btnGroup.length; i += 1) {
    btnGroup[i].addEventListener('click', function (e) {
      console.log(e.target.getAttribute('data-value'));
    })
  }
</script>

事件代理 Event delegation

  • 利用事件傳遞的機制,把 button 的 eventListener 綁定在上層的 .outer 元素上,就叫做 事件代理
<div class="outer">
  <button class="btn_add" >add</button>
  <button class="btn" data-value="1">1</button>
  <button class="btn" data-value="2">2</button>
</div>
<script>
  document.querySelector('.outer').addEventListener('click',  function (e) {
  console.log(e.target.getAttribute('data-value'));
})
</script>

#DOM







Related Posts

MTR04_0722

MTR04_0722

MTR04_1018

MTR04_1018

Vue props 隨筆

Vue props 隨筆


Comments