事件冒泡、事件捕獲
介紹事件冒泡和事件捕獲,並解釋如何使用 addEventListener 來控制事件流。
什麼是事件冒泡?
事件冒泡是指當一個元素觸發事件時,事件會從這個元素逐層往外傳遞。例如,當你點擊按鈕時,事件會先在按鈕上觸發,然後傳到外層的 div
,再傳到更外層的元素(例如 body
)。這樣我們可以在較高層的元素上處理事件,而不用為每個小元素都加監聽器,從而減少重複代碼、提升性能和維護性。
1 | <div id="container"> |
1 | document.getElementById('container').addEventListener('click', () => { |
當你點擊按鈕時,控制台會先顯示 “Button clicked”,然後顯示 “Container clicked”,這就是事件冒泡的效果。
什麼是事件捕獲?
事件捕獲與冒泡相反,事件從最外層元素開始逐層往內傳遞,直到目標元素。例如,當你點擊 inner
這個 div
時,如果使用事件捕獲,事件會先從最外層的 outer
開始,再傳遞到 inner
。事件捕獲可以用來在事件到達目標元素之前進行特殊處理。
事件流的圖解
下面這個圖表展示了事件捕獲和事件冒泡的過程:
- 捕獲階段(Capture Phase):事件從
document
開始,逐層往下傳遞,直到目標元素。 - 目標階段(Target Phase):事件到達目標元素。
- 冒泡階段(Bubbling Phase):事件從目標元素逐層往上傳遞,直到
document
。
使用 addEventListener
在 JavaScript 中,我們可以使用 addEventListener 為元素添加事件監聽器。addEventListener 方法有三個主要參數以及一個可選的物件參數:
- 事件類型(例如
click
):指定要監聽的事件。 - 事件處理函數:當事件發生時執行的函數。
- 可選參數(捕獲或冒泡):布爾值,決定事件是在捕獲階段還是冒泡階段處理。
true
表示捕獲,false
或不傳表示冒泡。 - 可選參數(
options
):這是一個物件,可以包含以下屬性:capture
:與原本用來表示「捕獲」或「冒泡」的機制相同。once
:代表這個事件只會被觸發一次,結束後就自動解除事件監聽。passive
:當設定成 true 時,表示這個事件處理器不會呼叫event.preventDefault()
這個方法。如果開發者呼叫了event.preventDefault()
時,瀏覽器會直接忽略,並在 console 主控台顯示警告訊息。
event.preventDefault()
用於阻止元素的預設行為。
例如,點擊一個連結時,它通常會跳轉到新的頁面;使用event.preventDefault()
可以阻止這種行為。
同樣地,在提交表單時也可以用來防止頁面重新加載。如果開發者呼叫了event.preventDefault()
時,瀏覽器會直接忽略,並在 console 主控台顯示警告訊息。
例如:
1 | document.getElementById('button').addEventListener('click', myClickHandler, { |
在這個例子中,button
的點擊事件只會觸發一次,並且在觸發後會自動移除,同時設置為被動模式以提升性能。
事件冒泡與捕獲的範例
1 | <div id="outer"> |
1 | document.getElementById('outer').addEventListener('click', () => { |
當你點擊 inner
這個 div
時,控制台會先顯示 “Outer Div clicked”,再顯示 “Inner Div clicked”,因為 outer
使用了捕獲階段。
停止事件傳遞
有時候,我們希望事件不要繼續傳遞,可以使用 stopPropagation()
方法。
1 | document.getElementById('inner').addEventListener('click', (e) => { |
當你點擊 inner
這個 div
時,事件不會再往外傳,因此 “Outer Div clicked” 不會顯示。
總結
- 事件冒泡:事件從目標元素逐層往外傳遞。
- 事件捕獲:事件從外層元素逐層往內傳遞。
- addEventListener:可以用第三個參數決定事件處理的階段(捕獲或冒泡)。
- stopPropagation():用來阻止事件的傳遞。