DOM Event Flow
在開發 UI 比較複雜的網站時,新手開發者很容易遇到類似的問題,必須真正瞭解整個 Event 的運作才可以解決。
在 DOM Event
運作中,分別有三種階段:
- Capture Phase(補獲)
- AT_TARGET
- Bubbling Phase(冒泡)
另外這三個階段在 Event Interface 都有設定成常數。
const unsigned short CAPTURING_PHASE = 1;
const unsigned short AT_TARGET = 2;
const unsigned short BUBBLING_PHASE = 3;
DOM Event
在運作時,先從最根部的節點開始往下傳遞到 target
(以 click 事件為例的話,就是點擊目標),這個階段叫做 Capture Phase
。
當到達了目標以後,事件會再從 target
由內往外傳回根節點,這個階段叫做 Bubbling Phase
。
(圖片來源:W3C event flow)
addEventListener
在 Javascript addEventListener
method 的第三個參數為 useCapture
,是用來決定 Event method
要在哪一個階段執行(預設是 Bubbling Phase)。
範例
<ul id="list">
<li id="list-item">
<a id="list-item-link" href="https://blog.johnsonlu.org/">Link</a>
</li>
</ul>
// 使用 Capture Phase
let isCapturePhase = true;
let list = document.querySelector("#list");
let listItem = document.querySelector("#list-item");
let listItemLink = document.querySelector("#list-item-link");
list.addEventListener('click', function (e) {
console.log('list bubbling', e.eventPhase);
}, isCapturePhase);
listItem.addEventListener('click', function (e) {
console.log('list-item bubbling', e.eventPhase);
}, isCapturePhase);
listItemLink.addEventListener('click', function (e) {
console.log('list-item-link bubbling', e.eventPhase);
e.preventDefault();
}, isCapturePhase);
停止事件冒泡
在 Event method
中,如果使用 return false
的話,會做三件事:
- 執行
event.preventDefault()
- 執行
event.stopPropagation()
- 傳回值並停止函式
Method | Features |
---|---|
preventDefault | 停止預設行為(例如點擊 <a> ,不會導到 href 的網址)。 |
stopPropagation | 停止事件傳導 |
範例
<ul id="list">
<li id="list-item">
<a id="list-item-link" href="https://blog.johnsonlu.org/">Link</a>
</li>
</ul>
list.addEventListener('click', function (e) {
console.log('list bubbling', e.eventPhase);
}, false);
listItem.addEventListener('click', function (e) {
console.log('list-item bubbling', e.eventPhase);
}, false);
listItemLink.addEventListener('click', function (e) {
console.log('list-item-link bubbling', e.eventPhase);
// 網頁不會導到 https://blog.johnsonlu.org/
e.preventDefault();
// 停止事件傳導,只會執行這一個 Event method
e.stopPropagation();
}, false);