問(wèn)題:
如果一個(gè)元素和它的祖先元素注冊(cè)了同一類型的事件函數(shù)(例如點(diǎn)擊等), 那么當(dāng)事件發(fā)生時(shí)事件函數(shù)調(diào)用的順序是什么呢?
比如, 考慮如下嵌套的元素:
<div class="outer">
<div class="inner"></div>
</div>
兩個(gè)元素都有onclick的處理函數(shù). 如果用戶點(diǎn)擊了inner, inner和outer上的事件處理函數(shù)都會(huì)被調(diào)用. 但誰(shuí)先誰(shuí)后呢?
事件流:
事件流描述的是從頁(yè)面中接受事件的順序扶欣,但有意思的是暴凑,微軟(IE)和網(wǎng)景(Netscape)開發(fā)團(tuán)隊(duì)居然提出了兩個(gè)截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕獲流(event capturing)枫攀。
事件捕獲(event capturing)
網(wǎng)景公司提出的事件流叫事件捕獲流。
事件捕獲流的思想是不太具體的DOM節(jié)點(diǎn)應(yīng)該更早接收到事件鸵赖,而最具體的節(jié)點(diǎn)應(yīng)該最后接收到事件抚恒,針對(duì)上面同樣的例子,點(diǎn)擊按鈕巡蘸,那么此時(shí)click事件會(huì)按照這樣傳播:(下面我們就借用addEventListener的第三個(gè)參數(shù)來(lái)模擬事件捕獲流)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<button>
<p>點(diǎn)擊捕獲</p>
</button>
</div>
<script>
var oP=document.querySelector('p');
var oB=document.querySelector('button');
var oD=document.querySelector('div');
var oBody=document.querySelector('body');
oP.addEventListener('click',function(){
console.log('p標(biāo)簽被點(diǎn)擊')
},true);
oB.addEventListener('click',function(){
console.log("button被點(diǎn)擊")
},true);
oD.addEventListener('click', function(){
console.log('div被點(diǎn)擊')
},true);
oBody.addEventListener('click',function(){
console.log('body被點(diǎn)擊')
},true);
</script>
</body>
</html>
同樣我們看一下后臺(tái)的打印結(jié)果:
正如我們看到的奋隶,它是和冒泡流萬(wàn)全相反,從最不具體的元素接收到最具體的元素接收事件 body=>div=>button=>p
事件冒泡(event bubbling)
與事件捕獲相反, 事件開始時(shí)由最具體的元素接收悦荒,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body onclick="bodyClick()">
<div onclick="divClick()">
<button onclick="btn()">
<p onclick="p()">點(diǎn)擊冒泡</p>
</button>
</div>
<script>
function p(){
console.log('p標(biāo)簽被點(diǎn)擊')
}
function btn(){
console.log("button被點(diǎn)擊")
}
function divClick(event){
console.log('div被點(diǎn)擊');
}
function bodyClick(){
console.log('body被點(diǎn)擊')
}
</script>
</body>
</html>
點(diǎn)擊p元素打印結(jié)果:
DOM事件流:
DOM事件分0級(jí)和2級(jí):
1唯欣、0級(jí)分兩種:
- 行內(nèi)事件:在標(biāo)簽中寫事件
<input type="button" id="btn" value="按鈕" onclick="alert('123')">
- 元素.on事件名=函數(shù)
document.getElementById("btn").onclick = function () {
alert('123');
}
事件沒(méi)有1級(jí)DOM
2、DOM2級(jí)事件:添加和移除事件處理程序:addEventListener()和removeEventListener()搬味。
DOM2級(jí)(addEventListener)事件流包含3個(gè)階段境氢,事件捕獲階段、處于目標(biāo)階段碰纬、事件冒泡階段萍聊。 先(document)開始一路向下捕獲, 直到達(dá)到目標(biāo)元素, 其后再次從目標(biāo)元素開始冒泡.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn">DOM事件流</button>
<script>
var btn=document.getElementById("btn");
btn.onclick=function(event){
console.log("div 處于目標(biāo)階段");
};
document.body.addEventListener("click",function(event){
console.log("event bubble 事件冒泡");
},false);
document.body.addEventListener("click",function(event){
console.log("event catch 事件捕獲");
},true);
</script>
</body>
</html>
打印結(jié)果為:
就是這樣一個(gè)流程,先捕獲悦析,然后處理寿桨,然后再冒泡出去。
作為開發(fā)者, 可以決定事件處理器是注冊(cè)在捕獲或者是冒泡階段. 如果addEventListener的最后一個(gè)參數(shù)是true, 那么處理函數(shù)將在捕獲階段被觸發(fā); 否則(false)默認(rèn)冒泡, 會(huì)在冒泡階段被觸發(fā).
阻止冒泡事件:
w3c的方法是e.stopPropagation()强戴,IE則是使用e.cancelBubble = true
參考鏈接: 淺談js的事件冒泡和事件捕獲