JS 筆記 3.5:事件冒泡、事件委派、事件傳遞與停止傳遞

更新 發佈閱讀 15 分鐘

JS 筆記系列 03_活用 JavaScript DOM:網頁互動的關鍵

距離 JS 筆記第三章又過好久,終於要來寫3.5了。這次玩玩看事件冒泡 (Event Bubbling)、事件代表 (Event Delegation) 、 事件傳遞 (Propagation) 、停止傳遞(stopPropagation)。可以直接看參考影片:

JavaScript 事件傳遞 (Event Propagation)

JS 筆記 - 認識 DOM 文件物件模型 小小補充後面會提到 DOM (Document Object Model),簡而言之就是每個節點(標題、內文...)都可以獨立加監聽事件的感覺。


📌 基本概念一:事件傳遞(Event Propagation)

事件傳遞是指瀏覽器在「觸發某個事件」時,它的訊息是如何沿著 DOM 結構流動。這個流程包含三個階段:

  1. 捕獲階段(Capturing Phase):從外往內找目標元素。
  2. 目標階段(Target Phase):事件到達你實際點擊的元素。
  3. 冒泡階段(Bubbling Phase):從目標元素往上冒到文件根節點。
原圖來自:TimCodingBlog

原圖來自:TimCodingBlog

🧪 範例:觀察事件傳遞流程

<div id="outer">
<div id="middle">
<button id="inner">Click Me</button>
</div>
</div>
document.getElementById('outer').addEventListener('click', () => {
console.log('Outer - Capturing');
}, true); // 設為 true:在捕獲階段觸發

document.getElementById('middle').addEventListener('click', () => {
console.log('Middle - Bubbling');
}); // 預設 false:在冒泡階段觸發

document.getElementById('inner').addEventListener('click', () => {
console.log('Inner clicked');
});

✅ 預期結果(按下 button 時):

raw-image


📘 補充:true 表示捕獲階段處理;沒設就是冒泡。

再補充:如果直接貼我上面的程式碼,會無法把 JS 跟 HTML 連在一起。上面的程式碼只是方便理解內容,記得要有 從外部導入 JS 的概念,加上

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>Event Demo</title>
</head>
<body>
<div id="outer">
<div id="middle">
<button id="inner">Click Me</button>
</div>
</div>

<!-- JavaScript 引入 -->
<script src="script.js"></script>
</body>
</html>

為了更方便理解,我請 Copilot 幫我寫一個有顏色的互動版

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>事件傳遞示範</title>
<style>
#outer {
background-color: #f94144; /* 紅色 */
padding: 30px;
}

#middle {
background-color: #f9c74f; /* 黃色 */
padding: 30px;
}

#inner {
background-color: #43aa8b; /* 青綠色 */
padding: 20px;
border: none;
color: white;
font-weight: bold;
cursor: pointer;
}

body {
font-family: Arial, sans-serif;
}
</style>
</head>
<body>
<h2>事件傳遞示範:捕獲 vs 冒泡</h2>
<p>點擊按鈕,開發者工具(F12)會顯示事件順序。</p>

<div id="outer">
Outer 區域
<div id="middle">
Middle 區域
<button id="inner">Click Me</button>
</div>
</div>

<script src="script.js"></script>
</body>
</html>
document.getElementById('outer').addEventListener('click', () => {
console.log('🔴 Outer - Capturing');
}, true); // 捕獲階段

document.getElementById('middle').addEventListener('click', () => {
console.log('🟡 Middle - Bubbling');
}); // 預設:冒泡

document.getElementById('inner').addEventListener('click', () => {
console.log('🟢 Inner clicked');
});
raw-image


如果當你點擊「Click Me」:

👣 事件流程:

  1. Outer 捕獲階段先觸發(紅色區塊)
  2. 再來是 Inner 本身的事件(青綠色按鈕)
  3. 然後再經過 Middle 的冒泡階段(黃色區塊)

對了,如果有時間可以試試每個事件觸發時改變按鈕或區塊顏色

document.getElementById('outer').addEventListener('click', () => {

  console.log('🔶 Outer - Capturing');

  document.getElementById('outer').style.backgroundColor = 'yellow';

}, true); // 捕獲階段 改背景色

📌 基本概念二:事件委派/代表(Event Delegation)

事件代表指的是「把事件綁在父層容器」,而不是每個小元素都綁一次。這樣做有幾個好處:

  • 減少記憶體負擔
  • 新增的元素也能被監聽
  • 適用於動態列表、商品清單

🧪 範例:用事件代表監聽 button 點擊

<div id="container">
<button>Item 1</button>
<button>Item 2</button>
<button>Item 3</button>
</div>
document.getElementById('container').addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON') {
console.log(`${e.target.textContent} clicked`);
}
});

✅ 預期結果(點 Item 1):記得開 f12 看

Item 1 clicked

📘 補充:透過 e.target 判斷點擊的是哪個子元素。


📌 基本概念三:停止傳遞(stopPropagation)

當你只想讓事件處理在某一層結束,不往外冒泡(或捕獲),就用 e.stopPropagation()

🧪 範例:阻止父層 console 印出

<div id="parent">
<button id="child">Click</button>
</div>
document.getElementById('parent').addEventListener('click', () => {
console.log('Parent clicked');
});

document.getElementById('child').addEventListener('click', (e) => {
console.log('Child clicked');
e.stopPropagation(); // 停止冒泡,不讓 parent 執行
});

✅ 預期結果(按下 button):

Child clicked

📘 補充:如果沒加 stopPropagation(),則會是:

Child clicked Parent clicked


🏢 商業環境實際應用案例

✨ 情境:動態建立商品按鈕(不斷新增)

關鍵程式碼:

因為 ul 是「無序清單」(unordered list),會在網頁上占一個「空白」的位置。在練習的過程中,還是要加入一些設計(顏色)等等才看的到。

<ul id="product-list">
<!-- 商品按鈕動態產生 -->
</ul>
document.getElementById('product-list').addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON') {
console.log(`商品 ${e.target.dataset.id} 被點擊`);
}
});

// 假設動態新增商品
function addProduct(id) {
const btn = document.createElement('button');
btn.textContent = `商品 ${id}`;
btn.dataset.id = id;
document.getElementById('product-list').appendChild(btn);
}

addProduct(101);
addProduct(102);

----完整網頁程式碼----

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>商品動態事件委派</title>
<style>
body {
font-family: Arial, sans-serif;
}

#product-list {
padding: 20px;
border: 1px solid #ccc;
background-color: #f1f1f1;
}

button {
margin: 5px;
padding: 10px 15px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<h2>商品列表</h2>
<p>點擊任意按鈕看看 Console 輸出</p>

<ul id="product-list">
<!-- 商品按鈕動態產生 -->
</ul>

<script src="script.js"></script>
</body>
</html>
// 👂 綁定事件監聽器在父層 ul,使用事件委派處理動態按鈕
document.getElementById('product-list').addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON') {
console.log(`商品 ${e.target.dataset.id} 被點擊`);
}
});

// 🪄 建立動態商品按鈕
function addProduct(id) {
const btn = document.createElement('button');
btn.textContent = `商品 ${id}`; // 顯示在按鈕上的文字
btn.dataset.id = id; // 使用 dataset 傳遞商品編號
document.getElementById('product-list').appendChild(btn);
}

// ➕ 新增商品
addProduct(101);
addProduct(102);
addProduct(103);

✅ 點擊商品結果:

raw-image


總結 Event Delegation :只監聽一次,新增幾百個商品都沒問題。

補充:這裡個語法是e.target.tagName。(確定使用者點的目標後再做動作)

e 是事件物件,event 的縮寫,當你觸發 click 時,瀏覽器會自動幫你傳入一個包含這次點擊的細節的物件。

把這段想成是一位警衛:

  1. 他站在 #product-list 門口觀察(事件監聽器)
  2. 有人點進來,他會說:「你是誰?」(e.target
  3. 只有當你是「BUTTON」他才會做出回應(if 條件)
  4. 然後記錄你的身分(data-id)在日誌裡(console.log)

以上就是 JS 3.5 的學習筆記,謝謝指教!



留言
avatar-img
留言分享你的想法!
avatar-img
越南放大鏡 X 下班資工系
50會員
98內容數
雙重身份:越南放大鏡 X 下班資工系 政大東南亞語言學系是我接觸越南語的起點,畢業後找越南外派工作的生活跟資訊時,發現幾乎都是清單式的分享,很難身歷其境。所以我希望「越南放大鏡」可以帶讀者看到更多細節和深入的觀察。 - 下班資工系則是自學資工系的課程內容,記錄實際操作的過程,學習理論的過程。希望可以跟讀者一起成長。
2025/06/27
最近在思考把 Threads 貼文轉移到notion變成資料庫跟加上主題編號,但100多篇文真的很懶得複製貼上。於是我參考了以下文章,並搭配 Copilot 把細微的功能修改,約2小時內可完成。這篇文章的目的是補充原作者沒有截圖到的內容,並分享「一鍵擷取」的功能,把第一篇到最新一篇的文全部抓下來。
Thumbnail
2025/06/27
最近在思考把 Threads 貼文轉移到notion變成資料庫跟加上主題編號,但100多篇文真的很懶得複製貼上。於是我參考了以下文章,並搭配 Copilot 把細微的功能修改,約2小時內可完成。這篇文章的目的是補充原作者沒有截圖到的內容,並分享「一鍵擷取」的功能,把第一篇到最新一篇的文全部抓下來。
Thumbnail
2025/06/24
這篇開始進到跟網頁互動較密切相關的單元,我會學習DOM 跟常用的網頁設計指令。練的環境是 VS code,之前的環境是Colab,但Colab比較適合Python。 DOM(Document Object Model),文件物件模型 DOM 是瀏覽器幫你把 網頁HTML 變成 JavaScrip
Thumbnail
2025/06/24
這篇開始進到跟網頁互動較密切相關的單元,我會學習DOM 跟常用的網頁設計指令。練的環境是 VS code,之前的環境是Colab,但Colab比較適合Python。 DOM(Document Object Model),文件物件模型 DOM 是瀏覽器幫你把 網頁HTML 變成 JavaScrip
Thumbnail
2025/06/24
這篇文章提供一個逐步教學,教你如何使用 VS Code 和 Live Server 外掛建立一個簡單的網頁,並搭配 JavaScript 程式碼呈現互動效果。教學包含建立檔案、撰寫 HTML 和 JavaScript 程式碼,以及使用 Live Server 預覽網頁的步驟。
Thumbnail
2025/06/24
這篇文章提供一個逐步教學,教你如何使用 VS Code 和 Live Server 外掛建立一個簡單的網頁,並搭配 JavaScript 程式碼呈現互動效果。教學包含建立檔案、撰寫 HTML 和 JavaScript 程式碼,以及使用 Live Server 預覽網頁的步驟。
Thumbnail
看更多
你可能也想看
Thumbnail
還在煩惱平凡日常該如何增添一點小驚喜嗎?全家便利商店這次聯手超萌的馬來貘,推出黑白配色的馬來貘雪糕,不僅外觀吸睛,層次豐富的雙層口味更是讓人一口接一口!本文將帶你探索馬來貘雪糕的多種創意吃法,從簡單的豆漿燕麥碗、藍莓果昔,到大人系的奇亞籽布丁下午茶,讓可愛的馬來貘陪你度過每一餐,增添生活中的小確幸!
Thumbnail
還在煩惱平凡日常該如何增添一點小驚喜嗎?全家便利商店這次聯手超萌的馬來貘,推出黑白配色的馬來貘雪糕,不僅外觀吸睛,層次豐富的雙層口味更是讓人一口接一口!本文將帶你探索馬來貘雪糕的多種創意吃法,從簡單的豆漿燕麥碗、藍莓果昔,到大人系的奇亞籽布丁下午茶,讓可愛的馬來貘陪你度過每一餐,增添生活中的小確幸!
Thumbnail
JSDoc 全名是 JavaScript Documentation,顧名思義是為 JavaScript 所使用的 API 文件,在程式碼內透過註解的方式撰寫,運行後 JSDoc 會自動掃描註解內容,並生成一份網頁版的文件,對於沒有使用 Typescript 開發的專案,也
Thumbnail
JSDoc 全名是 JavaScript Documentation,顧名思義是為 JavaScript 所使用的 API 文件,在程式碼內透過註解的方式撰寫,運行後 JSDoc 會自動掃描註解內容,並生成一份網頁版的文件,對於沒有使用 Typescript 開發的專案,也
Thumbnail
本章節的目的是介紹在TypeScript中如何進行例外處理。涵蓋了例外處理的重要性、語法、常見異常類型以及如何主動觸發異常訊息及用戶自定義異常訊息。為讀者提供了全面而深入的了解,以提高程式的可靠性、提供更好的反饋、增加程式的容錯性以及改善程式的可讀性。
Thumbnail
本章節的目的是介紹在TypeScript中如何進行例外處理。涵蓋了例外處理的重要性、語法、常見異常類型以及如何主動觸發異常訊息及用戶自定義異常訊息。為讀者提供了全面而深入的了解,以提高程式的可靠性、提供更好的反饋、增加程式的容錯性以及改善程式的可讀性。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 寫到挑戰六覺得心累ㄌ,向來不是一個可以長久堅持好習慣的人,30 個挑戰聽起來很少,但如果要日復一日堅持下去其實好長r 😮‍💨 挑戰六透過 input 來 filter 從 api 拿回來的資料結
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 寫到挑戰六覺得心累ㄌ,向來不是一個可以長久堅持好習慣的人,30 個挑戰聽起來很少,但如果要日復一日堅持下去其實好長r 😮‍💨 挑戰六透過 input 來 filter 從 api 拿回來的資料結
Thumbnail
本章目的是為讀者提供有關如何設置JavaScript開發環境的知識,包括在瀏覽器、Node.js和各種編輯器和IDE中編寫和運行JavaScript的信息。此外,本章還介紹了如何架設本地開發伺服器以模擬實際的網頁環境。這些知識對於希望開發前端應用或後端服務的JavaScript開發者來說都是必要的。
Thumbnail
本章目的是為讀者提供有關如何設置JavaScript開發環境的知識,包括在瀏覽器、Node.js和各種編輯器和IDE中編寫和運行JavaScript的信息。此外,本章還介紹了如何架設本地開發伺服器以模擬實際的網頁環境。這些知識對於希望開發前端應用或後端服務的JavaScript開發者來說都是必要的。
Thumbnail
JavaScript是一種具有動態型別、弱型別、原型繼承等特性的高級腳本語言,應用範圍廣泛,包括前端開發、後端開發、移動應用等。它被各種公司和開源社區廣泛使用。學習JavaScript需要掌握ECMAScript標準、異步編程、模塊系統等知識。
Thumbnail
JavaScript是一種具有動態型別、弱型別、原型繼承等特性的高級腳本語言,應用範圍廣泛,包括前端開發、後端開發、移動應用等。它被各種公司和開源社區廣泛使用。學習JavaScript需要掌握ECMAScript標準、異步編程、模塊系統等知識。
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 透過 JS 控制 CSS 變數 今天的挑戰是要能透過滑動圖片上面的 <input type="range"> 來改變圖片的三個屬性。 以前在 CSS 裡面看到 var(--xxxx) 這種東西都很
Thumbnail
JavaScript30 傳送門:https://javascript30.com/ 透過 JS 控制 CSS 變數 今天的挑戰是要能透過滑動圖片上面的 <input type="range"> 來改變圖片的三個屬性。 以前在 CSS 裡面看到 var(--xxxx) 這種東西都很
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News