前言
JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
本日目標
瞭解addEventListener中事件的捕捉、傳遞(Event Bubbling/Capturing)以及一次性的事件監聽(Once)。

解析程式碼
HTML 部分
建立三層的div做為測試event listener的物件。
div(.one) : 淡紫色
div(.two) : 淡粉色
div(.three) : 橘色
1 | <div class="one"> |

JS 部分
- Event Bubbling
首先取得所有的div標籤,然後在每個div上都註冊cllick event listener並以logText()進行事件處理把div標籤的class屬性值印出來。
1 | const divs = document.querySelectorAll('div'); |
點擊最內層的div(橘色),console 印出的內容如下 :

由上面的結果,我們可以得知在 div(.three)捕捉到事件後,還會連帶向上觸發parents的event handler,這種由底部向上傳遞觸發event handler的機制稱為event bubbling。
傳遞順序 : div.three(橘色) -> div.two(淡粉色) -> div.one(淡紫色)
- Event Capturing
前面的Event Bubbling是從觸發事件的element開始向外層的parent element進行事件傳遞,而Event Capturing則是從觸發事件的element的最外層parent element向內進行傳遞。
要做到這一點,我們就必須使用到addEvenListener的第三個參數Options Object的capture屬性,這個屬性的預設值是false,我們只需要把它改成true即可。
1 | /*上略...*/ |
設定完後點擊div.three,console 印出結果如下 :

傳遞順序 : div.one(淡紫色) -> div.two(淡粉色) -> div.three(橘色)
capture : true,Event Capturing。
capture : false,Event Bubbling。
上方的Event Bubbling其實可被改寫如下 :
1 | /*上略...*/ |
- Event Propagation
那如果不想讓事件由內向外(bubbling)或由外向內(capturing)傳遞要怎麼辦呢? 我們可以在event handler裡面對Event呼叫stopPropagation(),讓事件不再繼續傳遞。
以阻止Event Bubbling為例,在event handler裡面,我們對event(e)呼叫stopPropagation()。
1 | /*上略...*/ |
然後點擊div.three,console 印出結果如下 :

由上可知呼叫stopPropagation()後,事件就沒有繼續傳遞。
- Event Once
addEvenListener的第三個參數Options Object的屬性除了capture之外,還有once這個屬性(預設是false),它可以用來指定是否在觸發一次事件處理後,就unbind event listener(讓事件監聽器失效)。
下面以按鈕作為例子 :
在網頁上放置一個button。
1 | <button>Button</button> |
透過 JS 取得button標籤,然後為其註冊click event listener並指定Options Object的once屬性為 true。
1 | const button = document.querySelector('button'); |
此時點擊button,視窗會顯示提示訊息,但若在未 reload 頁面的情況下二次點擊button,會發現視窗不再顯示提示訊息,也就是button失效了。

這個效果可以放在提交表單的按鈕上,用來防止使用者重複提交表單。
補充資料 :
EventTarget.addEventListener()
Event.stopPropagation()
[教學] 瀏覽器事件:Event Bubbling, Event Capturing 及 Event Delegation