該文章會(huì)講述DOM規(guī)范里的事件流動(dòng)的機(jī)制鸯两。你需要對(duì)瀏覽器事件相關(guān)的知識(shí)有最基本的了解。
考慮這么個(gè)例子:
<div>
<button id="btn">Click Me!</button>
</div>
哪怕一個(gè)web開發(fā)的初學(xué)者都會(huì)知道脏榆,當(dāng)我們鼠標(biāo)在button上點(diǎn)擊時(shí)缘厢,會(huì)在button上觸發(fā)一個(gè)click事件。但是:
- button是div的一個(gè)子Node;從界面上來(lái)看责语,在button里點(diǎn)擊相當(dāng)于在div里點(diǎn)擊炮障;那click事件也會(huì)觸發(fā)在div上嗎?
- 如果click事件也觸發(fā)在div上坤候,那它們會(huì)不會(huì)共用同一個(gè)事件對(duì)象胁赢?
- 如果click事件也觸發(fā)在div上,誰(shuí)的事件會(huì)先發(fā)生白筹?
- click事件還會(huì)在哪些元素上面觸發(fā)智末?
- 等等...
想解答上述問(wèn)題,我們需要理解事件(Event)一個(gè)很重要的機(jī)制:事件流動(dòng)(Event Flow)徒河。
事件流動(dòng)
DOM事件不單單只會(huì)在一個(gè)Element上觸發(fā)系馆,它還會(huì)流向其他Element。事件的流動(dòng)通常會(huì)經(jīng)歷這么三個(gè)階段:
捕獲階段 -> 目標(biāo)階段 -> 冒泡階段
"eventPhase"
“eventPhase”是“Event”下的一個(gè)屬性顽照,它指明當(dāng)前event屬于那一個(gè)階段由蘑。
“eventPhase”可能是一下其中一個(gè)值:
- Event.NONE,0代兵,沒有事件需要處理
- Event.CAPTURING_PHASE尼酿,1,捕獲階段
- Event.AT_TARGET植影,2裳擎,目標(biāo)階段,事件對(duì)象到達(dá)事件目標(biāo)上
- Event.BUBBLING_PHASE思币,3鹿响,冒泡階段
下面我們?cè)敿?xì)討論一下這三個(gè)階段。
捕獲階段(capture phase)
捕獲階段的定義如下(w3c):
The event object propagate through the target's ancestors from the defaultView to the target's parent.
事件對(duì)象在事件目標(biāo)的祖先中上到下順向傳播支救,從最頂層的defaultView到事件目標(biāo)的(直系)父元素抢野。
捕獲階段發(fā)生在整個(gè)事件流動(dòng)的開始拷淘。在這階段里事件會(huì)從父(主干)到子(分支)由上往下傳播各墨,被元素一層層地捕獲。
文章開頭的例子里面启涯,捕獲階段的click事件會(huì)依次在document贬堵、body、div上觸發(fā):
document 1
v
body 2
v
div 3
v
button
一般我們沒太大需要監(jiān)聽捕獲階段的事件结洼;如果你確實(shí)希望這么做黎做,需要將addEventListener
的第三個(gè)參數(shù)設(shè)置為true:
// 第三個(gè)參數(shù)設(shè)置是否為捕獲階段,默認(rèn)為false
element.addEventListener('click', function() {}, true)
目標(biāo)階段(target phase)
目標(biāo)階段的定義是(w3c):
The event object arrive at the event object's event target.
事件對(duì)象到達(dá)事件目標(biāo)松忍。
例子里面蒸殿,就是事件在button上觸發(fā)的。addEventListener
可以監(jiān)聽目標(biāo)階段的事件:
element.addEventListener('click', function() {})
如果事件是不可冒泡的,那整個(gè)事件流動(dòng)會(huì)到此為止宏所,不會(huì)發(fā)生下面的冒泡階段酥艳。
冒泡階段(bubble phase)
冒泡階段的定義如下(w3c):
The event object propagates through the target's ancestors in reverse order, starting with the target's parent and ending with the defaultView.
事件對(duì)象會(huì)在事件目標(biāo)的祖先元素里反向傳播,由開始的父元素到最后的defaultView(document)爬骤。
冒泡階段發(fā)生在最后充石,這也是我們最為熟悉的一個(gè)階段。在這階段里事件會(huì)從子(分支)到父(主干)逆向傳播霞玄,看起來(lái)像是一個(gè)水里的泡泡往上冒骤铃。
例子里面,冒泡階段的click事件會(huì)依次在div坷剧、body惰爬、document上觸發(fā):
document 3
^
body 2
^
div 1
^
button
"bubbles"
Event下的bubbles屬性標(biāo)明該事件是否為可冒泡的。一旦該值為false惫企,則說(shuō)明 evnet不可冒泡补鼻,那其流動(dòng)也會(huì)在第二階段“目標(biāo)階段”后就終止。
總結(jié)
若一個(gè)元素(div)是目標(biāo)元素(button)的祖先雅任,那事件對(duì)象會(huì)在該元素上觸發(fā)兩次:一次是捕獲階段(1)的风范,另一次是冒泡階段(3)的。當(dāng)事件對(duì)象在事件目標(biāo)元素(button)上觸發(fā)時(shí)沪么,事件流動(dòng)進(jìn)入了目標(biāo)階段(2)硼婿。
- 想監(jiān)聽捕獲階段的事件,可以這樣:
element.addEventListener('click', cb, true)
禽车,將第三個(gè)參數(shù)設(shè)置為true寇漫。 - 想監(jiān)聽冒泡階段的事件,可以這樣:
element.addEventListener('click', cb,)
殉摔,不使用第三個(gè)參數(shù)或?qū)⑵湓O(shè)置為false州胳。 - 而上述的任何一種監(jiān)聽方式都可以監(jiān)聽到目標(biāo)階段的事件。
最后逸月,你可以配合這個(gè)例子來(lái)確認(rèn)一下你的理解栓撞。
let divElement = document.querySelector('div')
let btnElement = document.querySelector('button')
document.body.addEventListener('click', event => {
console.log('Body Click in Bubble Phase.')
console.log('Event Phase: ' + event.eventPhase)
})
document.body.addEventListener('click', event => {
console.log('Body Click in Capture Phase.')
console.log('Event Phase: ' + event.eventPhase)
}, true)
divElement.addEventListener('click', event => {
console.log('Div Click in Bubble Phase.')
console.log('Event Phase: ' + event.eventPhase)
})
divElement.addEventListener('click', event => {
console.log('Div Click in Capture Phase.')
console.log('Event Phase: ' + event.eventPhase)
}, true)
btnElement.addEventListener('click', event => {
console.log('Button Click in Target Phase.')
console.log('Event Phase: ' + event.eventPhase)
})
btnElement.addEventListener('click', event => {
console.log('Button Click in Target Phase.')
console.log('Event Phase: ' + event.eventPhase)
}, true)
Body Click in Capture Phase.
Event Phase: 1
Div Click in Capture Phase.
Event Phase: 1
Button Click in Target Phase.
Event Phase: 2
Button Click in Target Phase.
Event Phase: 2
Div Click in Bubble Phase.
Event Phase: 3
Body Click in Bubble Phase.
Event Phase: 3