#每日一記#通過 GIF 理解 addEventListener、捕獲和冒泡

羅小黑寫寫文字發表於2018-04-12

每日一記 - 但並不日更

回想起自己學習事件模式那會兒,怎麼都記不住事件的傳播流程和 addEventListener 的設定細節,後來發現好的圖片更能能幫助我們理解邏,所以做了一個 GIF。

事件傳播模式

假設我們現在有三個DOM節點,並且假設 div 為根節點。(通常事件的捕獲會從根節點開始)

div -> p -> span

然後我們為這些DOM節點設定好監聽。

// 設定第三個引數為 true 則在捕獲階段觸發
div.addEventListener('click', null, true);
p.addEventListener('click', null, true);
span.addEventListener('click', null, true);

span.addEventListener('click', null);
p.addEventListener('click', null);
div.addEventListener('click', null);
複製程式碼

現在我們點選span,那麼click事件會被觸發,事件會從div進入捕獲階段,從父級向子級傳遞,到達事件目標後進入冒泡階段,從子級像父級傳遞。

(click)span => div -> p -> span -> span -> p -> div

普通流程

如果我們點選p,那麼事件目標為p元素,事件從div元素開始捕獲,並從p元素處折返變為冒泡。

(click)p => div -> p -> p -> div

普通流程

多次繫結

如果在一個節點上多次繫結同一個事件的監聽,它們會按照事件傳播流程進行(先捕獲後冒泡),如果所處流程一樣則按照先繫結先觸發的原則。

// 設定第三個引數為 true 則在捕獲階段觸發
div.addEventListener('click', null, true) // #1
div.addEventListener('click', null)       // #2
div.addEventListener('click', null, true) // #3

p.addEventListener('click', null, true);
p.addEventListener('click', null);
複製程式碼

(click)p => div#1 -> div#3 -> p -> p -> div#2

多次繫結

雖然申明的順序是div#1 div#2 div#3,但是捕獲先於冒泡,所以 div#1 div#3依次觸發,而div#2在冒泡過程中觸發。

stopPropagation

這是Event物件的一個方法,用來阻止事件進一步傳播。

// 設定第三個引數為 true 則在捕獲階段觸發
// #1
div.addEventListener('click', function (event) {
  event.stopPropagation();
}, true) 
div.addEventListener('click', null)       // #2
div.addEventListener('click', null, true) // #3

p.addEventListener('click', null, true);
p.addEventListener('click', null);
複製程式碼

(click)p => div#1 -> div#3

stopPropagation

使用了stopPropagation()之後,事件就不能進一步傳播了,即使是在div上,捕獲和冒泡被認為是兩個步驟,所以在捕獲階段傳播被阻止時同節點上的冒泡也不會觸發。

stopImmediatePropagation

這是Event物件的一個方法,一旦呼叫這個方法,則該元素上未觸發的監聽都不會被觸發,事件也不會進一步傳播。

現在我們在div上再多增加一個事件監聽,並把stopImmediatePropagation新增在捕獲事件中第二個觸發的監聽上。

// 設定第三個引數為 true 則在捕獲階段觸發
div.addEventListener('click', null, true) // #1
div.addEventListener('click', null)       // #2
// #3
div.addEventListener('click', function (e) {
  e. stopImmediatePropagation();
}, true)
div.addEventListener('click', null, true) // #4

p.addEventListener('click', null, true);
p.addEventListener('click', null);
複製程式碼

(click)p => div#1 -> div#3

stopImmediatePropagation

使用了stopImmediatePropagation()之後,連當前節點中等待觸發的監聽都沉默了。

擴充套件閱讀

在比較新的瀏覽器中,addEventListener支援更多引數配置,第三個引數型別支援object

target.addEventListener(type, listener[, options]);

這個 options 支援三個欄位

  • capture Boolean 是否在捕獲模式觸發
  • once Boolean 是否僅觸發一次
  • passive Boolean 是否使用被動模式

這裡展示一下once的效果

// 設定第三個引數為 true 則在捕獲階段觸發
div.addEventListener('click', null, true);
p.addEventListener('click', null, true);
span.addEventListener('click', null, true);

span.addEventListener('click', null);
p.addEventListener('click', null, {once: true}); // 在冒泡階段只觸發一次
div.addEventListener('click', null);
複製程式碼

(click)span => div -> p -> span -> span -> p -> div

(click)span => div -> p -> span -> span -> div

once

Demo

JSbin

羅小黑寫寫文字

如果喜歡文章 請留下一個贊~ 如果喜歡文章 分享給更多人~

掘金中關注我 在簡書中關注我

自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證) 轉載時請保留原文連結 以保證可及時獲取對文章的訂正和修改

相關文章