昨天遇到一段js代碼裏面的addEventListener方法竟然有三個參數,去谷歌搜了一下,覺得這個概念很有意思,值得總結一下寫成博客。
useCapture
addEventListener()中的第三個參數是Boolean類型的值,代表第二個參數handler是否在第一個參數Event的capture階段使用。
事件(Event)在事件目標(EventTarget)中的順序
當DOM元素之間有包含關係時,發生在其上的事件有兩種順序:
- Capturing
- Bubbling
Capturing就是從上往下:
| |
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 \ / | |
| ------------------------- |
| Event CAPTURING |
-----------------------------------
這種情況下,事件發生後,註冊在element1上的handler會先觸發,註冊在element2上的handler會後觸發。
Bubbling就是從下往上:
/ \
---------------| |-----------------
| element1 | | |
| -----------| |----------- |
| |element2 | | | |
| ------------------------- |
| Event BUBBLING |
-----------------------------------
這種情況下,事件發生後,註冊在element2上的handler會先觸發,註冊在element1上的handler會後觸發。
W3C標準中的事件順序
| | / \
-----------------| |--| |-----------------
| element1 | | | | |
| -------------| |--| |----------- |
| |element2 \ / | | | |
| -------------------------------- |
| W3C event model |
------------------------------------------
在w3c模型中,事件會先從上往下,到事件目標元素後,再從下往上,一直到最外面的元素。
代碼實例
<div id="parent">parent div
<div id="child">child div</div>
</div>
const parent = document.querySelector('#parent');
const child = document.querySelector('#child');
function first() {
alert('first');
}
function second() {
alert('second');
}
parent.addEventListener('click', second, false);
child.addEventListener('click', first);
點擊child元素,會先彈出first,然後彈出second。我們來分析一下爲什麼會這樣:
click事件在child元素髮生後,第一步是capturing階段,先從上往下尋找child元素的錨定元素(Ancestor Element)中是否有可以觸發的handler,如果useCapture參數是true的話就會在此階段觸發,結果發現沒有可以觸發的handler。
click事件到達child元素後觸發first方法,然後開始bubbling階段,到達parent元素時發現有用於此階段的handler,觸發second方法。
如果我們把代碼改一下,變成:
parent.addEventListener('click', second, true);
child.addEventListener('click', first);
點擊child元素,會先彈出second,然後彈出first。分析過程和前面很類似:
click事件在child元素髮生後,第一步是capturing階段,先從上往下尋找child元素的錨定元素(Ancestor Element)中是否有可以觸發的handler,發現可以觸發second。
click事件到達child元素後觸發first方法,然後開始bubbling階段,到達parent元素時發現有用於此階段的handler,沒有發現可以觸發的handler。