「JS30紀錄&心得」25 - Event Capture, Propagation, Bubbling and Once

主題

解析addEventListener中事件的捕捉、傳遞、氣泡與單次執行方法

[DEMO][GitHub][JavaScript30全列表]

步驟

Step1. 建立事件模型與基本呼叫

首先建立三層DIV作為稍後測試使用的模型,
依序包覆為:紫色>淺橘色>深橘色

1
2
3
4
5
6
<div class="one"> 
<div class="two">
<div class="three">
</div>
</div>
</div>

接著建立click事件

1
2
3
4
5
6
7
8
9
10
// 取得頁面的所有div
const divs = document.querySelectorAll('div');

function logText(e) {
// 印出當前div的class name
console.log(this.classList.value);
}

// 為每個div加上click事件監聽
divs.forEach(div => div.addEventListener('click', logText));

Step2. 預設的點擊事件

當對著畫面中間(深橘色/one)做點擊時,console印出來的是

1
2
3
three
two
one

會從click的位置的最深處開始向外層連動所有的divclick事件,像是氣泡一樣的從內向外浮出去。

Step3. addEventListener的第三個參數-1:capture

深入檢查,會發現其實addEventListener是有第三個參數的:

1
2
3
divs.forEach(div => div.addEventListener('click', logText, {
capture: false, // 預設為false
}));

第三個參數的第一個屬性Capture就是事件的捕捉順序,
剛剛提到click後console印出來順序是由內向外,
若將Capture設為true會在點擊中間(深橘色/one)會印出:

1
one

就只有印出one而已,這是因為對當前最外層的容器one去點了,
就已經捕捉到目的了,所以他不會再往下找,只會到點擊的最外層目標為止。

Step4. stopPropagation()

但如果想從內層往外層點,而且是依選取層印出對應層級的話,
就要在列印的function加上topPropagation()來使用:

1
2
3
4
function logText(e) {
console.log(this.classList.value);
e.stopPropagation(); // stop bubbling!
}

這會使原本向外延伸的氣泡事件停止。

Step5. addEventListener的第三個參數-2:once

addEventListener的第三參數還有一個新屬性once
新增一個按鈕的click事件來測試:

1
2
3
4
5
button.addEventListener('click', () => {
console.log('Click!!!');
}, {
once: true
});

它可以使這個按鈕click被執行結束後,直接unbind這個元素與事件,
之後這個按鈕就已經不會再被觸發click事件了!
可以運用在很多避免重複點擊的狀況,例如表單送出後禁止user重複點擊。

其他

在一開始的練習JavaScript練習-臺北市旅遊景點時也有記錄到這個第三參數,
但當時只知道capture並不曉得once,對於整體的事件捕捉也不是很清楚,
經由這個練習範例加深了這個印象,雖然目前還是沒有正式運用到,
但這個觀念記著不吃虧,之後遇到相關問題會有更多可以思考的腦中資料:)。