[note] Event Capturing and Bubbling
tags: event
, propagation
, capturing
, bubbling
本篇非原創,內容整理自 :thumbsup: Bubbling and Capturing @ JavaScript.info。
冒泡事件(Bubbling)
冒泡(Bubbling):當一個元素被觸發時,在它身上事件處理器(event handler)會先執行,接著是它的父層元素,然後是所有其它的上層元素也都會被觸發。
舉例來說,當 <p>
被點擊時,<p>
的事件處理器會先被觸發,接著依序是 <div>
然後是 <form>
:
<!--
Event Bubbling: <p> -> <div> -> <form>
-->
<form onclick="alert('form')">
FORM
<div onclick="alert('div')">
DIV
<p onclick="alert('p')">P</p>
</div>
</form>
幾乎所有的事件都是冒泡事件,除了少數事件不是,如
focus
等。
因此最上層的元素幾乎可以知道內層元素所有發生的事情,其中包含幾個重要的屬性:
event.target
:觸發此事件的元素可以透過event.target
來取得,這個元素在整個冒泡過程中不會改變。event.currentTarget
:綁定此事件的元素,通常和this
指的是同一個元素。this
:指的是處理此事件的元素,和event.currentTarget
指稱同一個元素。
停止冒泡
冒泡事件會從被觸發該事件的 target element
一直上外傳遞,基本上會一直到 <html>
然後到 document
物件,有些事件甚至會到 window
。
event.stopPropagation()
:在冒泡的過程中,任何事件處理器都能夠決定要不要終止繼續向外冒泡,只需使用event.stopPropagation()
即可。event.stopImmediatePropagation()
:如果一個元素被綁了一個以上的事件處理器時,此時其中一個事件雖然使用event.stopPropagation()
來停止向外冒泡,但這個元素上的其他的事件處理器仍然會執行,此時可以使用event.stopImmediatePropagation()
來停止這個元素繼續冒泡,並避免這個元素上的其他事件處理器被執行。
stopPropagation()
會讓相同元素的其他 handlers 被執行,但是stopImmediatePropagation()
則會避免相同元素的其他事件再次被觸發。
要留意的是,除非有需要,否則不需要停止冒泡事件,舉例來說我們有一個 <ul>
選單,但如果在其中的 <li>
上使用了 event.stopPropagation()
的話,會使得 <ul>
沒辦法監聽到 <li>
的事件。
❗ 除非有需要,否則不需要停止冒泡事件。
捕獲事件(Capturing)
在事件處理的過程中還有一個稱作「捕獲(capturing)」的過程,但它實際上很少被用到。
在標準的 DOM 事件中,event propagation 可以分成三個階段:
- Capturing Phase:事件由外而內傳遞到被觸發事件的元素。
- Target Phase:事件抵達被觸發事件的元素。
- Bubbling Phase:事件從該元素透過冒泡從內而外傳遞。
舉例來說,當我們點擊 DOM 裡面的 <td>
時:
- Capturing Phase(紅色線):該事件會透過捕獲由外層傳進內層。
- Target Phase(藍色區塊):接著事件到達觸發此事件的元素。
- Bubbling Phase(綠色線):此事件透過冒泡又內向外傳遞。
當使用 addEventListener(event, handler)
的時候,預設只會處理到 event propagation 中的第二階段(target phase)和第三階段(bubbling phase),如果想要使用到第一階段(capturing phase),那麼就必須在 addEventListener()
中代入第三個參數為 true
(預設是 false
):
elem.addEventListener('click', (e) => alert(`Capturing: ${elem.tagName}`), true);
當我們把 addEventListener()
中的最後一個參數設為 true
時,整個流程會變這樣:
<!--
Event Capturing: <html> -> <body> -> <form> -> <div> -> <p>
Target Phase: <p>
Event Bubbling: <p> -> <div> -> <form> -> <body> -> <html>
-->
<form onclick="alert('form')">
FORM
<div onclick="alert('div')">
DIV
<p onclick="alert('p')">P</p>
</div>
</form>
檢視事件的傳遞階段:event.eventPhase
另外也可以使用 event.eventPhase
來取得此事件目前是在 event propagation 中的哪一個階段:
1
: 表示 CAPTURING_PHASE2
: 表示 AT_TARGET3
: 表示 BUBBLING_PHASE
範例程式碼
See the Pen [JS] Event Bubbling and Capturing by PJCHEN (@PJCHENder) on CodePen.
參考資料
- Bubbling and Capturing @ JavaScript.info
- DOM 的事件傳遞機制:捕獲與冒泡 @ TechBridge
- stopPropagation vs. stopImmediatePropagation @ StackOverflow