Event in DOM element

Event Object

  • Handler function 會帶的參數
1
2
3
4
const btn = document.queryselector('.btn');
btn.addEventlistener('click', (e) => {
// e is event object
});
  • e.target
    • 指向綁定該 handler 的 DOM (上例中即 btn)

Event propogation

兩個大原則

  • 先向下捕獲,再向上冒泡
  • 當事件傳到 target 本身,不分捕獲或冒泡

捕獲

  • 當一個事件(e.g. click)發生時, 事件會從 window 開始向下傳遞, 直到傳到 target

冒泡

  • 當事件傳到 target 後, 接著會向上冒泡, 直到回到 window

三個 Phase

  • event object 中包含了一個 propery: eventPhase
1
2
3
const unsigned short CAPTURING_PHASE = 1;
const unsigned short AT_TARGET = 2;
const unsigned short BUBBLING_PHASE = 3;
  • 當 Event 在向下捕獲時,Event Object 處於 CAPTURING_PHASE
  • 當 Event 到達 target 時,Event Object 處於 AT_TARGET
  • 當 Event 向上冒泡時,Event Object 處於 BUBBLING_PHASE


(From W3C - eventflow)

.addEventlistener() 的第三個參數

  • 決定這個 handler 要在何時執行

    • true: 在捕獲階段時執行
    • false(default): 在冒泡階段時執行
  • 直接看例子

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<body>
<ul id="list">
<li id="list_item">
<a id="list_item_link" target="_blank" href="http://google.com">
google.com
</a>
</li>
</ul>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const get = id => document.getElementById(id);
const $list = get('list');
const $list_item = get('list_item');
const $list_item_link = get('list_item_link');

// list 的捕獲
$list.addEventListener('click', (e) => {
console.log('list capturing', e.eventPhase);
}, true)

// list 的冒泡
$list.addEventListener('click', (e) => {
console.log('list bubbling', e.eventPhase);
}, false)

// list_item 的捕獲
$list_item.addEventListener('click', (e) => {
console.log('list_item capturing', e.eventPhase);
}, true)

// list_item 的冒泡
$list_item.addEventListener('click', (e) => {
console.log('list_item bubbling', e.eventPhase);
}, false)

// list_item_link 的捕獲
$list_item_link.addEventListener('click', (e) => {
console.log('list_item_link capturing', e.eventPhase);
}, true)

// list_item_link 的冒泡
$list_item_link.addEventListener('click', (e) => {
console.log('list_item_link bubbling', e.eventPhase);
}, false)

當按下超連結時的結果:

1
2
3
4
5
6
7
8
9
10
11
12
list capturing
1
list_item capturing
1
list_item_link capturing
2
list_item_link bubbling
2
list_item bubbling
3
list bubbling
3
  • 先執行了掛在 capture phase 的 handler, 接著執行 target 的 handler (不管是掛在 capturing or bubbling, 都屬於 AT_TARGET), 最後執行 bubling phase handler

  • [注意注意]

    • Event 在傳遞的過程中, 途中經過的各個 DOM handler 之中的 this指向該 DOM, 但是 e.target 均是指向最終的目標

e.stopPropagation()

  • 取消事件向下(or 向上)傳遞
  • 上述例子中修改為
1
2
3
4
$list.addEventListener('click', (e) => {
console.log('list capturing', e.eventPhase);
e.stopPropagation();
}, true)

Event 停止向下傳遞, 結果為

1
2
list capturing
1

e.preventDefault()

  • 取消預設行為
    • <a>: 點擊時超連結行為
    • <form>: 送出表單的行為
1
2
3
4
document.querySelector('a').addEventListener('click', (e) => {
e.preventDefault();
// a tag 的超連結行為失效
})
  • 當向下捕獲時有節點的 handler 執行了 e.preventDefault(), 之後接到該 Event 的節點都會被取消預設行為
    • 當上述例子在 #list 的 handler 加上 preventDefault, 事件傳到 #list_item_link 即失去了 <a> tag 超連結的預設行為

參考文獻