一凭戴、事件冒泡和捕獲
運行條件:當一個事件發(fā)生在具有父元素的的元素上時佩抹,現(xiàn)代瀏覽器根據(jù)事件添加時的設置來執(zhí)行(冒泡或者捕獲)
通過 addEventListener() 的第三個屬性來設置事件是通過捕獲階段注冊的(true),還是冒泡階段注冊的(false)帖世。默認情況下是false休蟹。
1、事件冒泡
從實際操作的元素(事件)向上級父元素一級一級執(zhí)行下去,直到達到
<html>
有些時候父元素和子元素都定義了click事件赂弓,但是不希望點擊子元素的時候執(zhí)行父元素的click事件(例如dialog彈窗的遮罩層如果是父元素绑榴,而dialog彈窗內(nèi)容層是子元素,同時可以通過點擊遮罩層來關閉彈窗盈魁,但是點擊內(nèi)容層不關閉彈窗)翔怎,可以通過stopPropagation()在子元素上阻止冒泡。
2备埃、事件捕獲(一般不會用到)
瀏覽器檢查元素的最外層祖先
<html>
姓惑,是否在捕獲階段中注冊了一個onclick事件處理程序,如果是按脚,則運行它于毙。
然后,它移動到<html>
中的下一個元素(點擊的元素的父元素)辅搬,并執(zhí)行相同的操作唯沮,然后是下一個元素(點擊的元素的父元素),依此類推堪遂,直到到達實際點擊的元素介蛉。
二、事件捕獲和冒泡的區(qū)別(執(zhí)行順序的不同)
// css
#div1 {
width: 400px;
height: 400px;
background: #f00;
}
#div2 {
width: 300px;
height: 300px;
background: #0f0;
}
#div3 {
width: 200px;
height: 200px;
background: #00f;
}
#div4 {
width: 100px;
height: 100px;
background: #f0f;
}
// html
<div id="div1">我是div1
<div id="div2">我是div2
<div id="div3">我是div3
<div id="div4">我是div4</div>
</div>
</div>
</div>
</body>
// javascript
var div1=document.getElementById("div1");
var div2=document.getElementById("div2");
var div3=document.getElementById("div3");
var div4=document.getElementById("div4");
div1.addEventListener("click",function(){
alert("我是div1");
})
div2.addEventListener("click",function(){
alert("我是div2");
})
div3.addEventListener("click",function(){
alert("我是div3");
})
div4.addEventListener("click",function(){
alert("我是div4");
})
冒泡的執(zhí)行順序:當我們點擊div1的時候溶褪,只彈出“我是div1”币旧,但是當我們點擊div2的時候,就先彈出“我是div2”猿妈,再彈出“我是div1”吹菱,當我們點擊div4 的時候,則是4-3-2-1這樣的順序彭则,這樣就叫做冒泡鳍刷,它就像魚兒吐泡泡一樣,從下到上俯抖,泡泡從水下上來泡泡會變得越來越大输瓜,從這個角度思考,先從子元素開始執(zhí)行芬萍,然后是父元素尤揣,再然后是祖先元素,他們的等級也是越來越大的担忧。在addEventListener里芹缔,false是默認的,表示的是冒泡瓶盛。
捕獲的執(zhí)行順序:在addEventListener里最欠,第三個參數(shù)設置為true示罗,表示的是捕獲。將上面的代碼中添加進第三個參數(shù)true芝硬,再運行代碼蚜点,會發(fā)現(xiàn)這時,當我們點擊div4的時候拌阴,出現(xiàn)的順序是1-2-3-4绍绘,它是從祖先元素開始慢慢找,最后找到我們的點擊目標迟赃,可以將這個行為理解為就像警察叔叔抓壞人一樣陪拘,逐漸的縮小抓捕范圍,最后確定到某一個人身上纤壁,所以這個過程叫做捕獲左刽。
dom元素中,既有冒泡酌媒,又有捕獲的執(zhí)行順序:w3c規(guī)定欠痴,任何發(fā)生在w3c事件模型中的事件,首是進入捕獲階段秒咨,直到達到目標元素喇辽,再進入冒泡階段。示例:
div1.addEventListener("click",function(){
alert("div1");
}, false);
div2.addEventListener("click",function(){
alert("div2");
}, true);
div3.addEventListener("click",function(){
alert("div3");
}, false);
div4.addEventListener("click",function(){
alert("div4");
}, true);
這時的div1和div3是冒泡事件雨席,div2和div4是捕獲事件菩咨,當我們點擊div4以后,彈出的順序是2-4-3-1陡厘。因為我們先執(zhí)行捕獲過程旦委,在這個例子中div2和div4是捕獲的,那么捕獲又是從大到小雏亚,所以,先彈出div2摩钙,再彈出div4罢低,捕獲結束以后就該是冒泡了。冒泡的順序是從小到大胖笛,從子到父网持,所以就先彈出div3,再就是div1长踊,所以最后的順序是2-4-3-1功舀。如果我們點擊div3,結果會是2-3-1身弊,同樣的道理辟汰,先捕獲列敲,捕獲是從div1開始到div3,這中間只有div2是捕獲帖汞,div4并沒有執(zhí)行到戴而,因為我們點擊的目標是div3,后面的步驟和前面的過程一樣翩蘸,先3后1所意。
最后一個問題,代碼示例:
div1.addEventListener("click",function(){
alert("div1");
}, false);
div2.addEventListener("click",function(){
alert("div2_捕獲");
}, true);
div2.addEventListener("click",function(){
alert("div2_冒泡");
}, false);
那么催首,當我們點擊div2的時候扶踊,結果是 div2_捕獲 -> div_2冒泡 -> div1的順序,代碼修改:
div1.addEventListener("click",function(){
alert("div1");
}, false);
div2.addEventListener("click",function(){
alert("div2_冒泡");
}, false);
div2.addEventListener("click",function(){
alert("div2_捕獲");
}, true);
這時當我們點擊div2的時候郎任,結果是 div2_冒泡 -> div_2捕獲 -> div1的順序
綜上結論:綁定在被點擊元素的事件是按照代碼的順序發(fā)生的秧耗,其他非綁定的元素則是通過冒泡或者捕獲的觸發(fā)。按照W3C的標準涝滴,先發(fā)生捕獲事件绣版,后發(fā)生冒泡事件。
事件的整體順序是:非目標元素捕獲 -> 目標元素代碼順序 -> 非目標元素冒泡歼疮。
三杂抽、事件委托
1、使用場景
如果你想要在大量子元素(包括動態(tài)添加的)中單擊任何一個就可以運行一段代碼韩脏,這個時候可以把事件監(jiān)聽器設置在父節(jié)點上缩麸。
2、好處
- 只在內(nèi)存中開辟了一塊空間赡矢,節(jié)省資源同時減少了dom操作杭朱,提高性能
- 對于新添加的元素也會有之前的事件
3、事件委托同捕獲和冒泡的關系
- 事件捕獲和冒泡是現(xiàn)代瀏覽器的執(zhí)行事件的兩個不同階段
- 事件委托是利用冒泡階段的運行機制來實現(xiàn)的
4吹散、實例:ul中觸發(fā)每個li來改變他們的背景顏色
// html
<ul id='ul'>
<li>111111</li>
<li>222222</li>
<li>333333</li>
</ul>
<button id='button'>添加元素</button>
// javascript
window.onload = function() {
let oUl = document.getElementById('ul');
let aLi = oUl.getElementsByTagName('li');
let but = document.getElementById('button');
let now = 3;
// 事件源:event對象弧械,不管在哪個事件中,只要你操作的哪個元素就是事件源
// ie:window.event.srcElement
// 標準:event.target
oUl.onmouseover = function(e){
let ev = e || window.event;
let target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
target.style.background = 'red';
}
}
oUl.onmouseout = function(e){
let ev = e || window.event;
let target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
target.style.background = '';
}
}
but.onclick = function(){
now ++;
let newLi = document.createElement('li');
newLi.innerHTML = 111111 * now;
oUl.appendChild(newLi);
}
}