iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 25

[Day25] - Event Capture, Propagation, Bubbling and Once(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

認識事件捕獲、冒泡、傳播、單次事件

  1. 認識事件:在開始程式碼之前先來認識,事件模型涉及的重要概念:事件捕獲、事件冒泡和事件傳播:

    1. 事件捕獲(Event Capturing)
    • 事件捕獲是事件處理的第一階段,它發生在事件到達實際目標元素之前。
    • 在事件捕獲階段,事件從根元素(通常是document)開始,然後逐級向下傳播到目標元素。
    • 在這個階段,可以註冊事件處理程序以捕獲事件(useCapture 於第三個參數帶入 true,預設為 false),並且這些處理程序會按照元素層次結構的順序執行,從根元素到目標元素。
    • 事件捕獲階段通常用得較少,而事件冒泡更常見。
    1. 事件冒泡(Event Bubbling)
    • 事件冒泡是事件處理的第二階段,它發生在事件已經觸發了目標元素上之後。
    • 在事件冒泡階段,事件從目標元素開始,然後逐級向上冒泡到根元素。
    • 大多數情況下,事件冒泡是默認的行為,通常我們註冊的事件處理程序時,都在冒泡階段執行。
    • 事件冒泡允許您在目標元素上觸發事件後,捕獲和處理該事件,並在父元素或更高層次的元素上進行進一步處理。
    1. 事件傳播(Event Propagation)
    • 事件傳播是事件在 DOM 樹結構中傳播的過程,包括事件捕獲和事件冒泡階段。
    • 在事件傳播過程中,事件在 DOM 樹的不同元素之間傳遞,直到到達目標元素並觸發事件處理程序。
    • 事件傳播的過程使您可以捕獲事件的各個階段,並根據需要執行相應的處理。

    總結來說,事件捕獲、事件冒泡和事件傳播是描述事件處理的不同階段和方式。事件捕獲從根元素向下捕獲事件,事件冒泡從目標元素向上冒泡事件,事件傳播包括這兩個階段,並提供了控制事件處理流程的靈活性。根據需求,您可以選擇在事件的不同階段進行處理,以實現不同的功能和互動。

  2. 接著我們來看程式碼

//  先取得作者設計好的三層div
const divs = document.querySelectorAll("div");
  • 先寫一個觸發事件監聽器會執行的函式:會印出觸發的 class 名稱方便哪裡發生了事件
function handleClick(e) {
  console.log(this.classList.value);
}
  • 將每個<div>都新增點擊事件監聽器
divs.forEach((div) => div.addEventListener("click", handleClick));
  • 然後點擊下圖中橘色區塊,你會發現,如上面所述,通常我們註冊的事件處理程序都發生在冒泡階段,因此觸發事件監聽器的順序是three > two > one
  • 接著我們在原本的事件監聽器後面帶入上第三個參數 useCapturetrue
divs.forEach((div) => div.addEventListener("click", handleClick, true));
  • 這時候一樣點擊橘色區塊,你會發現觸發事件監聽器的順序反過來了one > two > three顯然已經更改為捕獲階段觸發。

  • 想當然爾也可以兩個階段都有事件監聽器,執行結果如何就留給各位嘗試囉!

divs.forEach((div) => div.addEventListener("click", handleClick, true));
divs.forEach((div) => div.addEventListener("click", handleClick));
  1. 其他選項:事件監聽器的第三個參數其實不只useCapture只是如果你單獨使用一個布林值那麼瀏覽器會很聰明地幫你認定為useCapture設定,其他可選用的設定還有capture(同第三個參數只帶一個true)oncepassivesignal都必須包在一個物件內使用
  • capture(捕獲階段):如果設置為 true,事件將在捕獲被觸發。預設值為 false,表示事件在冒泡階段被觸發。

  • once(一次性監聽):如果設置為 true,事件監聽器在觸發後自動刪除

    以下範例為 capture 和 once 的結合

element.addEventListener("click", eventHandler, {
  capture: true,
  once: true,
});
  • passive(被動監聽):如果設置為 true,告訴瀏覽器該事件不會調用preventDefault(),从而可以进行一些性能优化。这在滚动事件等高频事件中很有用。
  • signal(AbortController 的 signal):允許使用 AbortController 信號來取消事件監聽器。這在某些情況下區要取消事件監聽器很有幫助。
const controller = new AbortController();
element.addEventListener("click", eventHandler, { signal: controller.signal });

// 取消事件監聽
controller.abort();
  1. 停止事件傳播:最後既然有事件傳播,那麼就有方法停止傳播,我只想要在觸發一次事件後阻止其他監聽器執行,那麼只要在 linstener 含式內寫下event.stopPropagation()即可。

    function handleClick(e) {
      //  停止事件傳播
      e.stopPropagation();
      console.log(this.classList.value);
    }
    

那麼今天就到這邊,大家還是要動手全部時做一次才會印象深刻哦!

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/
  3. DOM 事件傳遞機制:捕獲與冒泡、事件代理 by Heidi-Liu
    https://hackmd.io/@Heidi-Liu/note-fe201-dom

上一篇
[Day24] - Sticky Nav(JS30 x 鐵人 30 x MDN)
下一篇
[Day26] - Stripe Follow Along Nav(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言