之前學習冒泡和捕獲時有些囫囵吞棗,加之很長一點時間沒有做過dom類的工作,是以就遺忘了.這次因為項目中有一些dom操作需要我來寫,是以又需要去研究一下.故總結出一些我個人的見解跟大家分享.如有了解不對的地方,歡迎各位不吝賜教,批評佐證.
什麼是冒泡和捕獲
冒泡和捕獲是dom中的兩種事件流,現代浏覽器基本都遵循這兩種事件流方式.
上面這句話可能比較抽象,廢話不多說,那直接上代碼,讓我們通過現象去了解本質
<div id='far'>
<div id='son'></div>
</div>
//js es6
let far = document.getElementById('far');
let son = document.getElementById('son');
far.addEventListener('click',()=>console.log('我是far'));
son.addEventListener('click',()=>console.log('我是son'));
//執行以上代碼,當點選son時,你會發現far的點選事件也被觸發了.
很多人把這種現象叫做事件冒泡,起初我也是這樣認為的.但是當我學習了事件流中的冒泡模型和捕獲模型後,我就改變了想法(我認為,冒泡和捕獲隻是事件流執行的兩種規則,後面會具體說).因為當你在點選son的時候,其實你點選的位置也在far的範圍内,是以far的點選事件被觸發是正常的.
那麼也就可以很好的了解,為什麼當我們不給son注冊點選事件,而我們還是在son的範圍内點選滑鼠時,far依然會觸發.
那麼說到這裡,我們就可以談一談冒泡啦.
此時,我們需要先考慮一個問題:
為什麼先觸發的不是far,而是son呢?
要回答這個問題,首先我們要知道,當事件觸發時,事件會分為兩個階段,一個是冒泡階段,另一個則是捕獲階段.整個事件流當中,先執行捕獲,後執行冒泡.上面事例代碼執行結果的原因就是事件是在冒泡階段執行的(w3c在注冊事件當中規定預設情況下在冒泡階段執行).
在冒泡的階段的執行順序是按照 :從事件源 —>document 依次執行的
在剛才的例子當中的展現則是 son —>far(由内到外)
而捕獲階段則是: document—>事件源 依次執行
在下面例子中展現則是far—>son(從外到内)
//js es6
let far = document.getElementById('far');
let son = document.getElementById('son');
far.addEventListener('click',()=>console.log('我是far'),true);
son.addEventListener('click',()=>console.log('我是son'));
//addEventListenner函數的第三個參數可以控制事件在冒泡/捕獲階段執行. 當為false是,在冒泡階段執行,為true時,則在捕獲階段執行.
以上就是所謂的冒泡和捕獲.
為了更好的了解前面所說的内容,我們來通過一段代碼分析來加深了解
<ul id="ul">
<li id='one'>222</li>
<li id='two'><a href="#" id='only'>111</a></li>
<li id='three'>333</li>
</ul>
const ul = document.getElementById('ul');
const li = document.getElementById('two');
const a = document.getElementById('only')
a.addEventListener(
'click',
()=>console.log('我是a标簽'),
false);//冒泡階段執行
li.addEventListener(
'click',
()=>console.log('我是li标簽'),
false);//冒泡階段執行
ul.addEventListener(
'click',
()=>console.log('我是ul标簽'),
true);//捕獲階段執行
從以上代碼可以看出來, 三個dom元素具有子父級的關系
ul > li > a
當我點選a标簽時,執行結果如下:
ul —> a —>li
代碼分析:
因為我手動設定ul在捕獲階段執行,li 和 a 在冒泡階段執行.而前面我們講過,事件流是先執行捕獲後執行冒泡的.
說到這裡,相信大家已經能夠清晰的了解什麼是冒泡和捕獲了吧.
事件委托
我們來假設一個場景,我要給一個ul清單中的每一個li設定點選事件,應該怎麼做呢?
一般做法 : 就是給每一個li添加一個點選事件. 但是這樣做的弊端較大,備援代碼多,占用記憶體多等問題.
更好的辦法 : 使用事件委托. 利用我們前面說到的,給父元素注冊點選事件,點選内部子元素時,也會觸發父元素的事件.代碼如下:
<ul id="ul">
<li id='one'>111</li>
<li id='two'>222</li>
<li id='three'>333</li>
</ul>
let ul = document.getElementById('ul');
ul.addEventListener('click',(e)=>{
let ev = e || window.event;
let tag = ev.target||ev.srcElement;
switch(tag.id){
case 'one':
console.log("one")
break;
case 'two':
console.log('two')
break;
case 'three':
console.log('three')
break;
}
},true);
注:
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。