先舉一個例子,有一個父節(jié)點parent包含了一個子節(jié)點child,如下:
.outer{
border: 1px solid #ccc;
padding: 10px;
}
.inner{
border: 1px solid red;
padding: 10px;
}
<div id="parent" class='outer'>
parent
<div id="child" class='inner'>child</div>
</div>
const parentDom = document.getElementById('parent')
const childDom = document.getElementById('child')

事件流理解
DOM事件流包括三個階段:
- 事件捕獲:從最外層節(jié)點document逐級向下直到具體的事件對象,在該階段不會接收事件。
- 處于目標階段:事件在該階段發(fā)生,并看成冒泡階段的一部分。
- 事件冒泡:事件逐級向上傳播回文檔document。
如果對上述例子中的parent和child都定義click事件:
parentDom.addEventListener('click', (e)=>{
console.log('父節(jié)點點擊')
})
childDom.addEventListener('click', (e)=>{
console.log('子節(jié)點點擊')
})
如果點擊父節(jié)點,則只會調(diào)用父節(jié)點中定義的事件,打印內(nèi)容如下:

如果點擊子節(jié)點,則事件捕獲到具體的目標對象(子節(jié)點)后,調(diào)用對應(yīng)的事件,然后進入冒泡階段,父節(jié)點也會調(diào)用其click事件,因此打印內(nèi)容如下。

阻止事件冒泡
在上述問題中,如果我們點擊子節(jié)點時,只想觸發(fā)子節(jié)點的事件,不讓父節(jié)點的事件發(fā)生,則需要阻止事件冒泡。
childDom.addEventListener('click', (e)=>{
e.stopPropagation()
console.log('子節(jié)點點擊')
})
點擊子節(jié)點時,控制臺打印內(nèi)容:

如上所示,使用event對象的stopPropagation方法可以阻止事件冒泡,這樣就不會觸發(fā)父節(jié)點的click事件。
還有一個阻止事件冒泡的方法是stopImmediatePropagation,它與stopPropagation的區(qū)別為:
- 如果對同一個元素綁定了多個event事件,則
stopImmediatePropagation也會阻止該元素后面的event事件,而stopPropagation不會阻止同一個元素的后面的event事件。
如下,對childDom定義了兩個click事件的話,stopImmediatePropagation會阻止后面的事件調(diào)用,不會打印出 '子節(jié)點點擊第二次' ,而stopPropagation不會阻止,兩個都會打印。
childDom.addEventListener('click', (e)=>{
// e.stopPropagation()
e.stopImmediatePropagation()
console.log('子節(jié)點點擊第一次')
})
childDom.addEventListener('click', (e)=>{
console.log('子節(jié)點點擊第二次')
})
阻止事件的默認行為
有些事件會有一些默認的行為,比如a標簽點擊時會導(dǎo)航到href指定的url,如果想阻止這些默認行為,可以使用event的preventDefalut屬性。
<a href='' id='clickA'>點擊</a>
const aDom = document.getElementById('clickA')
aDom.addEventListener('click', (e)=>{
e.preventDefault()
})
上述這個例子點擊a標簽時就不會出現(xiàn)頁面刷一下的情況。
target與currentTarget區(qū)別
target是事件的目標,即上述事件流中處于目標階段的DOM節(jié)點。currentTarget是綁定事件的DOM節(jié)點,其實可以想象為bindTarget。如下:
parentDom.addEventListener('click', (e)=>{
console.log(e.target,e.currentTarget)
console.log('父節(jié)點點擊')
})
childDom.addEventListener('click', (e)=>{
console.log('子節(jié)點點擊')
})
則點擊子節(jié)點時,打印如下:

從上可知,target對象是child的DOM節(jié)點,而currentTarget是綁定事件的parent的DOM節(jié)點。