前言
當(dāng)我們點(diǎn)擊一個(gè)事件例如onclick, 這個(gè)事件發(fā)生后使兔,會(huì)在子元素和父元素之間傳播(propagation)管闷,這個(gè)過程就叫做事件傳播显歧。
事件傳播的三個(gè)階段
- 第一階段:從window對(duì)象傳導(dǎo)到目標(biāo)節(jié)點(diǎn)(上層傳到底層),稱為“捕獲階段”(capture phase)。
- 第二階段:在目標(biāo)節(jié)點(diǎn)上觸發(fā)颊糜,稱為“目標(biāo)階段”(target phase)。
- 第三階段:從目標(biāo)節(jié)點(diǎn)傳導(dǎo)回window對(duì)象(從底層傳回上層)秃踩,稱為“冒泡階段”(bubbling phase)衬鱼。
這種三階段的傳播模型,使得同一個(gè)事件會(huì)在多個(gè)節(jié)點(diǎn)上觸發(fā)憔杨。
xxx.addEventListener('click', callback, false);//冒泡 子傳父
xxx.addEventListener('click', callback, true);//捕獲 父?jìng)髯?
HMTL代碼片段:
<<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div class="red">
<div class="blue">
<div class="green">
<div class="yellow">
<div class="orange">
<div class="purple">
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
CSS代碼片段:
*{margin:0;padding:0;box-sizing:border-box;}
.red.active {
background: red;
}
.blue.active {
background: blue;
}
.green.active {
background: green;
}
.yellow.active {
background: yellow;
}
.orange.active {
background: orange;
}
.purple.active {
background: purple;
}
div {
border: 1px solid black;
padding: 10px;
transition: all 0.5s;
display: flex;
flex:1;
border-radius: 50%;
background: white;
}
.red{
width: 100vw;
height: 100vw;
}
JS片段:
let divs = $('div').get()
let n = 0
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.add('active')
}, n * 500)
n += 1
}, false)
}
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', () => {
setTimeout(() => {
divs[i].classList.remove('active')
}, n * 500)
n += 1
})
}
上面代碼中鸟赫,點(diǎn)擊子節(jié)點(diǎn)后父元素向子元素傳播,稱為捕獲階段消别,從子元素傳播到父元素抛蚤,稱為冒泡階段。
具體效果:http://js.jirengu.com/zayipuvafo/1/edit?html,css,js,output
出現(xiàn)這種情況會(huì)對(duì)我們開發(fā)者并不友好寻狂,那么我會(huì)想辦法在我們想要的位置停止傳播岁经,那就用到事件代理
事件代理
由于事件會(huì)在冒泡階段向上傳播到父節(jié)點(diǎn),因此可以把子節(jié)點(diǎn)的監(jiān)聽函數(shù)定義在父節(jié)點(diǎn)上蛇券,由父節(jié)點(diǎn)的監(jiān)聽函數(shù)統(tǒng)一處理多個(gè)子元素的事件缀壤。這種方法叫做事件的代理(delegation)。
var ul = document.querySelector('ul');
ul.addEventListener('click', function (event) {
if (event.target.tagName.toLowerCase() === 'li') {
// some code
}
});
上面代碼中纠亚,click事件的監(jiān)聽函數(shù)定義在<ul>節(jié)點(diǎn)塘慕,但是實(shí)際上,它處理的是子節(jié)點(diǎn)<li>的click事件蒂胞。這樣做的好處是图呢,只要定義一個(gè)監(jiān)聽函數(shù),就能處理多個(gè)子節(jié)點(diǎn)的事件骗随,而不用在每個(gè)<li>節(jié)點(diǎn)上定義監(jiān)聽函數(shù)岳瞭。而且以后再添加子節(jié)點(diǎn),監(jiān)聽函數(shù)依然有效蚊锹。
如果希望事件到某個(gè)節(jié)點(diǎn)為止瞳筏,不再傳播,可以使用事件對(duì)象的stopPropagation方法牡昆。
// 事件傳播到 p 元素后姚炕,就不再向下傳播了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, true);
// 事件冒泡到 p 元素后摊欠,就不再向上冒泡了
p.addEventListener('click', function (event) {
event.stopPropagation();
}, false);
上面代碼中,stopPropagation方法分別在捕獲階段和冒泡階段柱宦,阻止了事件的傳播些椒。
但是,stopPropagation方法只會(huì)阻止事件的傳播掸刊,不會(huì)阻止該事件觸發(fā)<p>節(jié)點(diǎn)的其他click事件的監(jiān)聽函數(shù)免糕。也就是說,不是徹底取消click事件忧侧。
p.addEventListener('click', function (event) {
event.stopPropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 會(huì)觸發(fā)
console.log(2);
});
上面代碼中石窑,p元素綁定了兩個(gè)click事件的監(jiān)聽函數(shù)。stopPropagation方法只能阻止這個(gè)事件向其他元素傳播蚓炬。因此松逊,第二個(gè)監(jiān)聽函數(shù)會(huì)觸發(fā)。輸出結(jié)果會(huì)先是1肯夏,然后是2经宏。
如果想要徹底阻止這個(gè)事件的傳播,不再觸發(fā)后面所有click的監(jiān)聽函數(shù)驯击,可以使用stopImmediatePropagation方法烁兰。
p.addEventListener('click', function (event) {
event.stopImmediatePropagation();
console.log(1);
});
p.addEventListener('click', function(event) {
// 不會(huì)被觸發(fā)
console.log(2);
});
上面代碼中,stopImmediatePropagation方法可以徹底阻止這個(gè)事件傳播徊都,使得后面綁定的所有click監(jiān)聽函數(shù)都不再觸發(fā)沪斟。所以,只會(huì)輸出1碟贾,不會(huì)輸出2币喧。
小結(jié):
事件傳播不可以避免,但是我們調(diào)用事件代理來得到我們想要的方案袱耽。自己也對(duì)dom事件模型有了更深的理解杀餐。