範例

為了解釋事件的三大階段,使用下面三個方框

紅: box-1
綠: box-2
黃: box-3

使用事件監聽每一個方框,當點擊時每個方框時會印出哪個box被點擊了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
let box1 = document.getElementById('box-1');
let box2 = document.getElementById('box-2');
let box3 = document.getElementById('box-3');


box1.addEventListener('click', () => {
console.log('box1 has been clicked');
});

box2.addEventListener('click', () => {
console.log('box2 has been clicked');
});

box3.addEventListener('click', () => {
console.log('box3 has been clicked');
});
</script>

印出結果:

  • 當點擊紅框框顯示出
    box-1 has been clicked

  • 當點擊綠框框顯示出
    box-1 has been clicked
    box-2 has been clicked

  • 當點擊黃框框顯示出
    box-1 has been clicked
    box-2 has been clicked
    box-3 has been clicked

捕捉階段 Capture Phase

紅色線條的階段是事件捕捉階段,在這個階段會依序從Window往下找直到目標為止

目標階段 Target Phase

發現監聽目標準備要發泡回去

冒泡階段 Bubbling Phase

從監聽目標開始發泡回去Windows,我們範例這邊就是抓取這段作說明解釋:

當點擊黃框框會造成三個事件都觸發是因為黃色是目標階段往父母層的綠以及紅也都被觸發了所以他們都被印出內容

如何停止冒泡階段

使用stopPropagation()就可以讓這樣污染的情況停止

1
2
3
4
box3.addEventListener('click', (e) => {
e.stopPropagation();
console.log('box3 has been clicked');
});

preventDefault()有用處嗎?

preventDefault()只能停止預設的行為,對於停止冒泡還是得使用stopPropagation()

1
2
3
4
5
box3.addEventListener('click', (e) => {
e.preventDefault();
// e.stopPropagation();
console.log('box3 has been clicked');
});

印出結果:

點擊box-3還是會顯示三個事件的結果

事件委派 Event delegation

用下方的無順序列表來示範事件委派:

想要點擊個別的li時(也就是111, 222, 333)會印出其內容

1
2
3
4
5
<ul id="list">
<li id="li-1">111</li>
<li id="li-2">222</li>
<li id="li-3">333</li>
</ul>

比較笨的寫法就是全部上事件監聽絕對沒錯,可是當有一萬個li就不可能這樣實現這時候就可以使用事件委派 Event delegation

1
2
3
4
5
6
7
8
9
li1.addEventListener('click', (e) => {
console.log(e.target);
})
li2.addEventListener('click', (e) => {
console.log(e.currentTarget);
})
li3.addEventListener('click', (e) => {
console.log(e.target.innerText);
})

使用事件的委派技巧使用li的父母層也就是list直接抓取li就算li數量再多都不用擔心瞜!

1
2
3
4

list.addEventListener('click', (e) => {
console.log(e.target.innerText);
});

Target, currentTarget 的區別

  • currentTarget
    會印出當前事件附屬於哪個元素或是觸發事件監聽的元素

比較特別的是如果有父母層存在會先印出父母層(沒有的話則印出當下印出觸發事件監聽的元素)

  1. 會印出事件的父母層
  2. 當下印出觸發事件監聽的元素
1
2
3
list.addEventListener('click', (e) => {
console.log(e.currentTarget);
});
  • target
  1. 當下印出觸發事件監聽的元素
    1
    2
    3
    list.addEventListener('click', (e) => {
    console.log(e.currentTarget);
    });