Event Capture & Bubble
先說說 Capture (捕捉) 與 Bubble (冒泡),要看清楚兩著的差異,最直接的方法就是圖解,直接上圖吧!
Capture Phase:
DOM 傳遞順序是由上到下(從 window 到 document 一路下傳到被點擊的 div)
Bubble Phase:
DOM 傳遞順序是由下到上(從被點擊的 div 往上傳到 document 再到 window)
假設我們對一個 DOM (某 button or 任何 node) 添加一個 click event,並點擊它後產生的傳遞順序,是捕捉還是冒泡呢?
答案是: “先捕捉再冒泡”
什麼原因造成的呢? 要從歷史來說,會產生兩種不同的傳遞順序,是因為當年瀏覽器角逐時期,Microsoft & Netscape 兩巨頭分別提出的兩種不同事件流處理法,而最後 W3C 做的決定是兩種都支援,並把兩種合在一起,才導致現在的瀏覽器同時存在這兩種方式,目前把先捕捉再冒泡當成 “預設”,所以假設當一個 event 發生時,會產生的傳遞順序如下圖 :
這也應證了上面的答案: "先捕捉再冒泡";然而我們前面說到是 "預設" 的情況下,也就是說這個傳遞順序是可以改變的,接著透過前述說到的addEventListener & stopPropagation &preventDefault 來看看吧!
addEventListener
它在做什麼大家都很熟了,不再贅述,如果還不了解的小夥伴可以參考[MDN-addEventListener],接著我們將聚焦它的第三個參數上
target.addEventListener(type, listener, options); // 第三個是 Boolean
當 options 參數設定的 Boolean 值不同時,它對傳遞順序的改變,如下:
True: 把這個 listener 添加到捕獲階段 (Capture)。
False: 把這個 listener 添加到冒泡階段 (Bubble)。
沒給: 使用預設,把這個 listener 添加到冒泡階段 (Bubble)。
弄清楚了 Event Capture & Bubble 的傳遞順序以及特性之後,我們常見的一個問題就是當綁定了 addEventListener DOM 的父層,有相同的綁定事件時會因為冒泡的關係一同被觸發,如下:
當查看 console 時會發現,明明只按了 child 卻連 parent 一起被觸發
這很顯然不是我們要的,後面來說說怎麼避免
stopPropagation & preventDefault
我們想要阻擋冒泡亂觸發父層事件,那麼就可以利用 event object 提供的 stopPropagation,輕鬆解決
上述例子還有一個小問題,就是都是只用 div,那如果是用 from、a 等等 html 元素,他們本身就已經有預設行為,那當想修改時該怎麼做呢? 這時就可以用 event 給的 preventDefault,來中止預設行為
但要注意的是, preventDefault 雖然會中止預設行為,但並不會阻止事件冒泡!!! 所以要停止冒泡還是要用 stopPropagation 喔!