1哈误、事件傳播機(jī)制暖眼、阻止傳播惕耕、取消默認(rèn)事件、事件代理這些到底是什么呢诫肠?
①事件傳播機(jī)制:
JS事件傳播包括三個(gè)階段:
捕獲階段:簡(jiǎn)單來(lái)說(shuō)就是從最頂層元素司澎,逐漸進(jìn)入dom內(nèi)部欺缘,查找目標(biāo)元素的過(guò)程。過(guò)程中依次執(zhí)行各個(gè)元素綁定在捕獲階段的事件(不包括目標(biāo)元素上的事件)
處于目標(biāo)階段:到達(dá)目標(biāo)元素挤安,按事件注冊(cè)順序執(zhí)行綁定在目標(biāo)元素上的事件
冒泡階段:從目標(biāo)元素谚殊,依次像外層元素冒泡,最后到達(dá)頂層元素的過(guò)程蛤铜。依次執(zhí)行各個(gè)元素綁定在冒泡階段的事件(不包括目標(biāo)元素上的事件)
②阻止傳播:即阻止事件進(jìn)一步的捕獲或者冒泡嫩絮,使事件不在向后傳播∥Х剩可以使用事件對(duì)象e(e的介紹在我的博客JS DOM(2)的第6點(diǎn)中已經(jīng)介紹)的stopPropagation()設(shè)置在事件的相應(yīng)階段剿干。
舉個(gè)列子:列子代碼地址
列子中嵌套了3個(gè)div(div#outer =》 div#middle =》div#inner)。給每個(gè)div都綁定了捕獲階段和冒泡階段的事件穆刻。
(1)首先測(cè)試捕獲階段設(shè)置阻止事件傳播怨愤。把div#middle的捕獲階段的代碼改成如下:
middle.addEventListener("click", function(e){
e.stopPropagation();
console.log("middle捕獲");
}, true);
點(diǎn)擊div#inner,出現(xiàn)如下結(jié)果蛹批,可見(jiàn)div#middle的捕獲階段后面的事件都不執(zhí)行了,事件被阻止傳播了
"outer捕獲"
"middle捕獲"
(2)接著測(cè)試在處于目標(biāo)階段設(shè)置阻止事件傳播篮愉。在div#inner的冒泡階段事件中設(shè)置阻止事件傳播(目標(biāo)元素的事件執(zhí)行是按照注冊(cè)順序執(zhí)行腐芍,和冒泡捕獲無(wú)關(guān),我的div#inner的冒泡事件是設(shè)置在捕獲事件的前面的试躏,正常不阻止事件傳播的話猪勇,會(huì)先輸出"inner冒泡",在輸出"inner捕獲")
inner.addEventListener("click", function(e){
e.stopPropagation();
console.log("inner冒泡");
}, false);
點(diǎn)擊div#inner颠蕴,結(jié)果如下:☆此時(shí)尤其需要注意☆泣刹,處于目標(biāo)階段的所有事件都會(huì)被執(zhí)行完
"outer捕獲"
"middle捕獲"
"inner冒泡"
"inner捕獲"
(3)最后測(cè)試冒泡階段設(shè)置阻止事件傳播。在div#middle的冒泡階段設(shè)置阻止事件傳播
middle.addEventListener("click", function(e){
e.stopPropagation();
console.log("middle冒泡");
}, false);
點(diǎn)擊div#inner犀被,結(jié)果如下:可見(jiàn)div#middle的冒泡階段后面的事件都不執(zhí)行了椅您,事件被阻止傳播了
"outer捕獲"
"middle捕獲"
"inner冒泡"
"inner捕獲"
"middle冒泡"
③取消默認(rèn)事件:
對(duì)于一些元素是有默認(rèn)事件的,比如點(diǎn)擊a鏈接可以實(shí)現(xiàn)跳轉(zhuǎn)寡键。又比如表單中掀泳,點(diǎn)擊input類型為submit的按鈕會(huì)默認(rèn)提交表單。
那么如何取消這些默認(rèn)事件呢西轩?
事件對(duì)象e中有個(gè)preventDefault()方法员舵,給這些具有默認(rèn)事件的元素重新綁定事件,并且在里面設(shè)置e.preventDefault()就可以取消默認(rèn)事件了
還是以代碼為例:
<a >baidu</a>
<script>
document.querySelector('a').onclick = function(e){
e.preventDefault()
console.log(this.href)
if(/baidu.com/.test(this.href)){
location.href = this.href
}
}
</script>
<form action="/login">
<input type="text" name="username" placeholder="login">
<input type="submit" value="login">
</form>
<script>
document.querySelector('form').addEventListener('submit', function(e){
e.preventDefault()
if(document.querySelector('input[name=username]').value === 'login'){
this.submit()
}
})
</script>
④事件代理:
事件代理是利用事件的冒泡原理來(lái)實(shí)現(xiàn)的藕畔。當(dāng)我們需要對(duì)很多元素添加事件的時(shí)候马僻,可以為每個(gè)元素都綁定事件。也可以給他們的父節(jié)點(diǎn)綁定事件注服。因?yàn)槭录芭菥碌耍?dāng)點(diǎn)擊子節(jié)點(diǎn)的時(shí)候措近,事件會(huì)冒泡傳播到父節(jié)點(diǎn),觸發(fā)設(shè)置在父節(jié)點(diǎn)上的事件仍秤。這就是事件代理熄诡,委托它們父級(jí)代為執(zhí)行事件。
為什么要用事件代理?
假設(shè)有100個(gè)li诗力,每個(gè)li都有相同的click點(diǎn)擊事件凰浮,如果用for循環(huán)的方法,來(lái)遍歷所有的li苇本,然后給它們添加事件袜茧,需要添加100事件。每個(gè)函數(shù)都是一個(gè)對(duì)象瓣窄,是對(duì)象就會(huì)占用內(nèi)存笛厦,對(duì)象越多,內(nèi)存占用率就越大俺夕,自然性能就越差了裳凸。而且每次增加或刪除具有相同事件的li都需要重新綁定或者解除事件。
如果用事件委托劝贸,那么我們就可以只對(duì)它的父級(jí)(如果只有一個(gè)父級(jí))這一個(gè)對(duì)象進(jìn)行操作姨谷,這樣我們就需要一個(gè)內(nèi)存空間就夠了,是不是省了很多映九,自然性能就會(huì)更好梦湘。而且每次增加或刪除需要有相同事件的li不需要做額外操作。
那么如何在父節(jié)點(diǎn)的事件中獲取到具體點(diǎn)擊的是哪個(gè)子節(jié)點(diǎn)呢件甥?
Event對(duì)象提供了一個(gè)屬性叫target捌议,e.target可以返回事件的目標(biāo)節(jié)點(diǎn)。e.target.nodeName可以獲取具體是什么標(biāo)簽名引有,這個(gè)返回的是大寫(xiě)的(比如"LI")
以下代碼事例加深理解
事例實(shí)現(xiàn)了:
- 當(dāng)點(diǎn)擊按鈕開(kāi)頭添加時(shí)在<li>這里是</li>元素前添加一個(gè)新元素瓣颅,內(nèi)容為用戶輸入的非空字符串;當(dāng)點(diǎn)擊結(jié)尾添加時(shí)在最后一個(gè) li 元素后添加用戶輸入的非空字符串.
- 當(dāng)點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容譬正。
代碼地址
事例中點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容就是通過(guò)事件代理實(shí)現(xiàn)的弄捕,無(wú)論增加或刪除li都不需要再做額外的事件綁定或解除