初學者指南:JavaScript 中的事件階段(Event Capture, Propagation and Bubbling)

更新日期: 2024 年 10 月 25 日

在 JavaScript 中,事件處理是一個非常重要的部分。當我們與網頁互動時,會觸發各種事件(例如點擊、滑鼠移動、鍵盤按鍵等)。

這些事件會在網頁的不同階段進行傳遞,這個過程被稱為「事件傳遞階段」。

本文將帶你理解 JavaScript 事件傳遞的三個階段:捕獲階段(Capture)目標階段(Targeting)冒泡階段(Bubble),並介紹 e.targete.currentTarget 的差異,以及如何選擇使用它們。


事件傳遞的三個階段

當我們點擊網頁中的某個元素時,事件會按照一定的順序在整個文檔中傳遞,這個過程分為三個階段:捕獲階段(Capture)目標階段(Targeting)冒泡階段(Bubble)

這三個階段描述了事件從上層節點到目標元素,再回到上層節點的整個過程。

捕獲階段(Capture Phase)

捕獲階段是事件從最上層節點(如 document)開始,一層層地往下傳遞,直到目標元素的階段。

這個過程就像事件從上往下「捕獲」到目標一樣。

在捕獲階段,我們可以攔截事件,並根據需要處理它。

目標階段(Target Phase)

目標階段是事件抵達目標元素的階段,事件在這個階段直接作用於我們實際點擊的元素(或任何觸發事件的元素)。

這個階段的處理主要針對具體的目標元素,並且可以使用 JavaScript 事件物件中的 e.target 來取得這個目標元素。

e.target 的作用
當事件發生時,e.target 指的是實際觸發事件的元素。例如,如果我們點擊了一個按鈕,e.target 就是這個被點擊的按鈕。通過 e.target,我們可以訪問並操作該元素的屬性,例如改變按鈕的文字內容或樣式。

範例:

document.getElementById('myButton').addEventListener('click', function(e) {
  console.log(e.target);  // 會輸出被點擊的按鈕元素
});

在這裡,e.target 是指我們點擊的按鈕 button,通過它我們可以獲取目標元素的信息。

冒泡階段(Bubble Phase)

冒泡階段是事件從目標元素向最上層節點傳遞的階段,類似氣泡從水底「冒出」到水面的過程。

這意味著事件會從目標元素開始,逐層往上回傳,直到達到 document 節點。

在冒泡階段,我們也可以攔截事件並進行處理。


圖式化說明

讓我們用圖式化的方式來理解這三個階段:

捕獲階段(Capture Phase)
document -> html -> body -> div -> button

目標階段(Target Phase)
button (目標元素)

冒泡階段(Bubble Phase)
button -> div -> body -> html -> document

假設我們有如下的 HTML 結構,並且我們在頁面上的按鈕進行點擊:

<html>
  <body>
    <div id="container">
      <button id="myButton">點擊我</button>
    </div>
  </body>
</html>

在這個範例中,如果你點擊了按鈕 button,事件的傳遞順序將是:

  1. 捕獲階段:事件從 document 開始,依次往下傳遞到 htmlbodydiv,最終到達 button
  2. 目標階段:事件在目標元素(button)上觸發,並可通過 e.target 獲取被點擊的元素。
  3. 冒泡階段:事件從 button 開始,依次往上傳遞回 divbodyhtml,最終到達 document

使用 addEventListener 處理事件

在 JavaScript 中,我們通常使用 addEventListener() 方法來為一個元素添加事件監聽器。

這個方法有三個參數:

element.addEventListener(event, handler, useCapture);
  • event:事件的名稱,例如 'click'
  • handler:處理事件的回調函數。
  • useCapture:可選參數,表示監聽器在哪個階段被觸發。如果設置為 true,表示監聽器在捕獲階段被觸發;如果設置為 false(或不設置),表示監聽器在冒泡階段被觸發。

預設情況是在冒泡階段

addEventListener 的第三個參數 useCapture 是可選的,默認值為 false,這意味著默認情況下,事件監聽器是在冒泡階段執行的。

如果你希望在捕獲階段處理事件,你需要明確將這個參數設置為 true

範例:

// 在冒泡階段添加監聽器(預設情況)
document.getElementById('myButton').addEventListener('click', function() {
  console.log('在冒泡階段觸發');
}, false);

// 在捕獲階段添加監聽器
document.getElementById('myButton').addEventListener('click', function() {
  console.log('在捕獲階段觸發');
}, true);

在這裡,我們給按鈕添加了兩個點擊事件監聽器,一個在冒泡階段觸發(默認),另一個在捕獲階段觸發。

這樣你就可以觀察到事件在不同階段的觸發順序。


e.targete.currentTarget 的區別

在處理事件時,我們常用到 e.targete.currentTarget 這兩個屬性,它們有著不同的用途。

e.target

  • 定義e.target 指的是實際觸發事件的元素
  • 用途:當你想知道是哪個元素引發事件時,可以使用 e.target。它可以用來操作目標元素的屬性,例如改變文字或樣式。

範例:

document.getElementById('container').addEventListener('click', function(e) {
  console.log('e.target:', e.target);  // 會輸出被點擊的具體元素
});

如果點擊的是 buttone.target 會指向這個 button

e.currentTarget

  • 定義e.currentTarget 指的是綁定事件監聽器的元素。也就是說,當事件觸發並傳遞到綁定的監聽器時,e.currentTarget 就是那個綁定事件的元素。
  • 用途:當你希望獲取當前處理事件的元素時,可以使用 e.currentTarget,例如對父元素進行操作,特別是在有多層圖層堆疊的情況下,需要精準控制綁定事件的圖層時非常有用。

範例:

假設你有一個多層堆疊的圖層結構,你希望每次點擊某個圖層時,只針對被綁定事件的特定圖層進行操作。

<div id="layer1" class="layer">圖層 1
  <div id="layer2" class="layer">圖層 2
    <div id="layer3" class="layer">圖層 3</div>
  </div>
</div>
document.querySelectorAll('.layer').forEach(layer => {
  layer.addEventListener('click', function(e) {
    console.log('e.currentTarget:', e.currentTarget);  // 輸出被綁定事件的圖層
    // 例如,只改變綁定事件的圖層背景色
    e.currentTarget.style.backgroundColor = 'lightblue';
  });
});

在這個例子中,無論你點擊哪一個圖層內的部分,`e.currentTarget

` 總是指向綁定了事件的那個圖層,而不是深層的子元素。這在需要精準控制每個圖層的行為時非常有用。


事件階段的應用場景

使用捕獲階段的情況

  • 全局攔截:有時你需要在事件到達特定元素之前進行處理,例如阻止某些特定的事件傳遞到目標元素。

    在這種情況下,你可以使用捕獲階段來攔截事件。
  • 事件過濾:如果你希望在事件傳遞到目標元素之前進行某種條件判斷,捕獲階段會很有用。

使用冒泡階段的情況

  • 事件委派:冒泡階段最常見的應用場景是事件委派

    這意味著你可以把事件監聽器綁定到一個父元素,當子元素上發生事件時,事件會冒泡到父元素,從而觸發父元素上的監聽器。這樣可以減少事件監聽器的數量,並使代碼更加簡潔和高效。

小結

在 JavaScript 中,事件的傳遞分為三個階段:捕獲階段(Capture)目標階段(Target)冒泡階段(Bubble)

理解這些階段可以幫助你更好地掌握事件處理的原理,並靈活地控制事件的觸發和傳遞。

  • 捕獲階段:事件從 document 開始,一層層往下傳遞到目標元素。
  • 目標階段:事件在目標元素上觸發,可以通過 e.target 獲取目標元素並進行操作。
  • 冒泡階段:事件從目標元素開始,一層層往上傳遞到 document

在使用 addEventListener() 時,默認情況下事件監聽器是在冒泡階段觸發的(useCapturefalse)。如果需要在捕獲階段觸發,可以將 useCapture 設置為 true

理解 e.targete.currentTarget 的區別,並根據需要靈活應用它們,將有助於更好地處理和管理網頁中的事件行為。

希望這篇文章能幫助你更好地理解 JavaScript 事件階段,以及如何有效使用這些屬性來處理各種事件。

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *