javascriptFebruary 25, 2018

瀏覽器事件:Event Bubbling, Event Capturing 及 Event Delegation

這篇教學介紹瀏覽器事件的運作原理,包含event bubbling、event capturing。第二部分介紹event delegation的寫法及應用時機。教學內容主要參考javacript.info 的 Introduction into Events。

目錄

Event Bubbling

Event Bubbling指的是當某個事件發生在某個DOM element上(如:點擊),這個事件會觸發DOM element的event handler,接下來會再觸發他的parent的event handler,以及parent的parent的event handler…直到最上層。

以下的例子中,點擊p會依序觸發p -> div -> form的onclick handler。

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P
    </p>
  </div>
</form>

Parent element的event handler中可以得到實際觸發事件的DOM element的資訊。實際觸發event的DOM element稱為target。上面的例子中,點擊p時target就是p,點擊div時target就是div,以此類推。

Event handler中可以透過event.target物件存取target。而event handler中,如果要存取event handler綁定的DOM element本身,可以透過this或是event.currentTarget存取。(如果需要bind this到別的東西的情況可用event.currentTarget。)

form.onclick = function(event) {
  console.log(event.target) // the element that is actually clicked, say p
  console.log(event.currentTarget) // form
}

Event capturing

和Event bubbling相反,event發生在某個DOM element上的時候,會從他的最上層parent開始觸發capturing handler,再來是倒數第二上層的ancestor的capturing handler,以此類推,直到觸發事件的DOM element本身的capturing handler。

如果要設定capturing handler,將addEventListener的第三個參數設為true(預設值為false):

form.addEventListener('click', handler, true)

因此,如果把event bubbling和event capturing的機制一起看的話,順序會是由上而下的Event capturing -> 由下而上的Event bubbling,所以依序會觸發的event handler是:

  1. Capturing (form)
  2. Capturing (div)
  3. Capturing (p)
  4. Bubbling (p)
  5. Bubbling (div)
  6. Bubbling (form)

Event Delegation

Event delegation指的是:假設同時有很多DOM element都有相同的event handler,與其在每個DOM element上個別附加event handler,不如利用event bubbling的特性,統一在他們的ancestor的event handler處理。

舉個例子,假設有個 list,按下去的時候要刪除 item:

<ul id="todoList">
  <!-- The ID is stored in the data-id attribute -->
  <li class="todo-item">
    <button class="delete-btn" data-id="123">
      <span>Delete</span>
    </button>
  </li>
  <li class="todo-item">
    <button class="delete-btn" data-id="456">
      <span>Delete</span>
    </button>
  </li>
</ul>

雖然可以對個別的 button 附加click event handler,但也可以透過event delegation的方式,統一在 ul 處理:

list?.addEventListener('click', e => {
  if (!(e.target instanceof Element)) return;

  const btn = e.target.closest<HTMLButtonElement>(
    "button.delete-btn"
  );

  if (!btn || !list.contains(btn)) return;

  const productId = btn.dataset.id;
  if (!productId) return;

  deleteProduct(productId);
})

即使動態增加list item,也不用寫額外的code幫他們新增click event handler,是不是挺方便的!

Behavior Pattern

我們可以利用event delegation的機制,幫element增加”行為”。做法是:

  1. 在需要某種行為的DOM element增加attribute。
  2. 在最上層的document增加event listener,並檢查元件是否帶有特定attribute。
Counter: <input type="button" value="1" data-counter>

<script>
  document.addEventListner('click', e => {
    if (typeof e.target.dataset.counter !== undefined) {
      e.target.value++
    }
  })
</script>

Reference

Introduction into Events - javascript.info


shubo資深前端工程師,最近在探索 AI 時代的軟體開發方式
© 2026 shubo