FE Interview Hub
瀏覽器原理中階

請說明瀏覽器中的事件委派、捕獲、冒泡

AI 練習作答

事件傳播的三個階段

當你點擊一個 DOM 元素,事件並不是只在那個元素上發生,而是會經歷三個階段:

Window
  └─ Document
       └─ <html>
            └─ <body>
                 └─ <div>  ← 1. 捕獲階段(由上而下)
                      └─ <button>  ← 2. 目標階段(on target)
                 └─ <div>  ← 3. 冒泡階段(由下而上)

1. 捕獲階段(Capture Phase)

事件從 window 向下傳播到目標元素。addEventListener 第三個參數設為 true 可在此階段監聽。

parent.addEventListener('click', handler, true); // 捕獲

2. 目標階段(Target Phase)

事件到達實際被點擊的元素,捕獲與冒泡監聽器都會在此執行。

3. 冒泡階段(Bubble Phase)

事件從目標元素向上傳播window。預設 addEventListener 在此階段監聽。

child.addEventListener('click', handler);        // 冒泡(預設)
child.addEventListener('click', handler, false); // 同上

不是所有事件都會冒泡:focusblurloadscroll 等預設不冒泡。

停止傳播

  • e.stopPropagation():停止事件繼續向上冒泡(或向下捕獲)。
  • e.stopImmediatePropagation():停止傳播,並阻止同一元素上後續的監聽器執行。
child.addEventListener('click', (e) => {
  e.stopPropagation(); // 不讓事件冒泡到 parent
});

事件委派(Event Delegation)

利用冒泡的特性,把多個子元素的事件統一綁定在父元素上。

優點

  • 減少事件監聽器數量,節省記憶體
  • 動態新增的子元素自動受到監聽,不需重新綁定

範例

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>
</ul>
// 不要這樣做(每個 li 都綁一個)
document.querySelectorAll('#list li').forEach(li => {
  li.addEventListener('click', handleClick);
});

// 應該這樣(委派給父層)
document.getElementById('list').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    console.log('點擊了:', e.target.textContent);
  }
});

取消預設行為

e.preventDefault() 取消瀏覽器的預設行為(如 <a> 跳頁、<form> 送出),與阻止冒泡是不同的概念。

小結

概念 方向 預設監聽
捕獲 window → 目標 addEventListener(_, _, true)
目標 兩者都會觸發
冒泡 目標 → window addEventListener(_, _)
委派 利用冒泡在父元素統一處理

✦ AI 模擬面試

輸入你的答案,AI 即時分析精準度與改進空間

登入後即可使用 AI 評分