事件作為DOM操作中重要的一個(gè)點(diǎn),好好的理解并運(yùn)用相當(dāng)?shù)闹匾?/p>
1呻顽、事件冒泡
簡(jiǎn)單的說(shuō)雹顺,就是當(dāng)一個(gè)子元素的事件被觸發(fā)的時(shí)候(如onclick事件),該事件會(huì)從事件源(被點(diǎn)擊的子元素)開(kāi)始逐級(jí)向上傳播廊遍,觸發(fā)父級(jí)元素的點(diǎn)擊事件嬉愧。
<div id="parent" style="background-color: #000;height: 400px;width: 400px" data-id="11">
<div id="child" style="background-color: #fff;height: 200px;width: 200px" data-id="22"></div>
</div>
document.getElementById('parent').onclick=function () {
console.log(this.getAttribute('data-id'));
};
document.getElementById('child').onclick=function (e) {
console.log(this.getAttribute('data-id'));
};
在線運(yùn)行: http://jsbin.com/ritivosoco/edit?html,js,console,output
這里我們可以發(fā)現(xiàn),當(dāng)你點(diǎn)擊白色區(qū)域(子元素)昧碉,父級(jí)的click事件也被觸發(fā)啦(觀察控制臺(tái)的輸出)英染,那么這里就有一個(gè)問(wèn)題啦揽惹,可不可以當(dāng)點(diǎn)擊子元素的時(shí)候不觸發(fā)父元素的點(diǎn)擊事件啊被饿,這就是接下來(lái)要說(shuō)的啦
2、阻止事件冒泡
所謂阻止事件冒泡其實(shí)啊搪搏,就是我們不讓點(diǎn)擊這個(gè)事件傳遞給父元素啊狭握,哈哈,這就要用到事件中的一個(gè)stopPropagation()方法啦,還是先看代碼吧
document.getElementById('parent').onclick=function () {
console.log(this.getAttribute('data-id'));
};
document.getElementById('child').onclick=function (e) {
console.log(this.getAttribute('data-id'));
e.stopPropagation();
};
在線運(yùn)行: http://jsbin.com/ritivosoco/edit?html,js,console,output
嘿嘿疯溺,此刻你在線上運(yùn)行的時(shí)候是不是發(fā)現(xiàn)當(dāng)我們點(diǎn)擊白色區(qū)域的時(shí)候并沒(méi)有觸發(fā)到父元素的點(diǎn)擊事件啊论颅,這是因?yàn)槲覀冏柚沽俗釉氐氖录芭堇玻?/p>
朋友們有可能會(huì)覺(jué)得事件冒泡真是煩人,寫(xiě)個(gè)事件還要阻止冒泡囱嫩!不過(guò)凡事都有雙刃劍恃疯,事件冒泡同時(shí)給我們帶來(lái)的還有事件委托這一減少DOM操作的神器,接下來(lái)介紹哦
3墨闲、事件委托
事件委托今妄,首先按字面的意思就能看你出來(lái),是將事件交由別人來(lái)執(zhí)行,再聯(lián)想到上面講的事件冒泡盾鳞,是不是想到了犬性?對(duì)啦,其實(shí)就是將子元素的事件通過(guò)冒泡的形式交由父元素來(lái)執(zhí)行腾仅。
可能在開(kāi)發(fā)的時(shí)候會(huì)遇到這種情況:如導(dǎo)航每一個(gè)欄目都要加一個(gè)事件乒裆,你可能會(huì)通過(guò)遍歷來(lái)給每個(gè)欄目添加事件,首先我們來(lái)看一段不使用事件委托的代碼, 我們遍歷子元素?cái)?shù)組:
<ul id="parent">
<li>孩子1</li>
<li>孩子2</li>
<li>孩子3</li>
<li>孩子4</li>
<li>孩子5</li>
</ul>
var ul = document.getElementById('parent')
var lis = ul.getElementsByTagName('li')
for (var i = 0; i<lis.length;i++){
lis[i].onclick= function() {
alert(this.innerHTML);
}
}
這種方式看起來(lái)直接明了推励,但需要多次操作dom(每點(diǎn)擊一個(gè)li都會(huì)操作一次dom)鹤耍,如果子元素較多的話則是一件相當(dāng)恐怖的事情,而且當(dāng)我們后面添加一個(gè)新的li子元素后验辞,新添加的li沒(méi)有綁定事件惰蜜,需要再次給新的li綁定一次,考慮到給新添加的元素綁定事件:
<ul id="parent">
<li>孩子1</li>
<li>孩子2</li>
<li>孩子3</li>
<li>孩子4</li>
<li>孩子5</li>
</ul>
var ul = document.getElementById('parent')
var lis = ul.getElementsByTagName('li')
function addClick() {
for (var i = 0; i<lis.length;i++){
lis[i].onclick= function() {
alert(this.innerHTML);
}
}
}
addClick()
function addElement() {
var li = document.createElement('li');
li.innerHTML="我是新孩子";
ul.appendChild(li);
addClick();
}
addElement();
上面寫(xiě)的代碼確實(shí)可以解決新添加子元素的問(wèn)題受神,但代碼就有點(diǎn)多了抛猖,而且本質(zhì)上并沒(méi)有改變DOM的操作次數(shù),優(yōu)化性能鼻听,下面我們直接來(lái)看事件委托的寫(xiě)法:
<ul id="parent">
<li>孩子1</li>
<li>孩子2</li>
<li>孩子3</li>
<li>孩子4</li>
<li>孩子5</li>
</ul>
var ul = document.getElementById('parent');
ul.onclick = function (e) {
//判斷只有l(wèi)i觸發(fā)的才會(huì)輸出內(nèi)容
if(e.target.nodeName.toLowerCase() === "li"){
alert(e.target.innerHTML);
}
e.stopPropagation();
}
在線運(yùn)行: http://jsbin.com/bamonabeda/16/edit?html,js,console,output
因?yàn)槭潜O(jiān)聽(tīng)的父元素财著,所有即使新添加元素也是可以默認(rèn)綁定好事件的,這樣我們可以看到代碼更簡(jiǎn)潔了撑碴,也減少了dom的操作次數(shù)撑教,是不是非常的優(yōu)雅和nice啊醉拓!
4伟姐、總結(jié)
最后總結(jié)下哈,事件冒泡是個(gè)雙刃劍亿卤,當(dāng)對(duì)我們有負(fù)面的影響的時(shí)候就顯得相當(dāng)討厭愤兵,不過(guò)我們可以用阻止冒泡來(lái)達(dá)到我們想要的效果;但是在另一方面在特定的場(chǎng)合我們又可以借助于事件冒泡來(lái)優(yōu)化我們的代碼排吴,優(yōu)化性能秆乳,只有對(duì)原理了解于心,才可以突破更多的限制钻哩。