一、事件傳播機制
想要深入瞭解委託,最好先深入理解事件傳播機制:
我們把事件分為三個階段:
捕獲階段
、目標階段
、冒泡階段
;
- 捕獲階段:當點選時,先經過捕獲階段,從最外層如html(這裡不考慮相容了)層層找到目標
- 目標階段:經過目標階段,響應事件
- 冒泡階段:從點選目標往外層層觸發相同的事件方法直到最外層(根節點)
觸發當前元素的某一個事件(點選事件)行為,不僅當前元素事件行為觸發,而且其祖先元素的相關事件行為也會依次被觸發,這種機制就是
“事件的冒泡傳播機制”
如圖:
如果有不太理解的可以看這個小?哦?:
比如某一天你在河邊玩耍,,走著走著,看到一個小石子,哼,把小石子往河裡一扔,河就泛起了層層波浪,對不對啦。這個時候就叫做冒泡
。
二、事件栗子(冒泡)
栗子
<div id="parent">
<div id="child">按鈕</div>
</div>
<script>
const parent=document.getElementById('parent');
const child=document.getElementById('child');
parent.addEventListener('click',function(){
console.log('parent');
})
child.addEventListener('click',function(){
console.log('child');
})
//經過上面的分析很容易得出結果,小朋友們你們自己算哦
</script>
複製程式碼
注意dom0和dom2事件機制不同;
dom0:在屬性上掛載,同一個元素只能有一個點選事件,多個點選事件,後者會覆蓋前者;
dom2:在EventTarget.prototype定義的,同一個元素多個點選事件不會覆蓋,都會執行;原理是有一個統一的事件池;觸發時,瀏覽器會把事件池中所有的按照存放順序發放
這裡就不多說啦啦啦啦,有一點偏離主題
三、委託的優點(經典例子)
1. 減少記憶體的消耗
因為繫結事件越多,瀏覽器記憶體佔用越大,嚴重影響效能 還是舉個?吧:
- 有100條資料,100個li,給每個li都加事件,佔用記憶體很大,所以,利用冒泡機制,在父元素上新增點選事件:如下
<ul id="ul">
</ul>
<script>
const ul=document.getElementById('ul');
for(let i=0;i<100;i++){
let li=document.createElement('li');
li.innerHTML=i;
ul.appendChild(li);
}
ul.addEventListener('click',function(e){
if(e.target.tagName==='UL') return;
e.target.className=e.target.className.indexOf('color')===-1?'color':'';
})//可自行執行;color是類名可以在style中新增自己喜歡的顏色,666
</script>
複製程式碼
2. ajax的出現,區域性重新整理的盛行,導致每次載入完,都要重新繫結事件(這裡就使用setTimeout非同步代替了)
<ul id="ul">
<li>666</li>
</ul>
<script>
const ul=document.getElementById('ul');
const lists=ul.getElementsByTagName('li');
setTimeout(()=>{
for(let i=0;i<100;i++){
let li=document.createElement('li');
li.innerHTML=i;
ul.appendChild(li);
}
},400)
for(let i=0;i<lists.length;i++){
lists[i].onclick=function(){
alert(i);
}
}
//結果只點選666彈窗;怎麼解決;大家動動腦子吧;相信你們是最棒的
</script>
複製程式碼
四、委託的侷限性
- 比如 focus、blur 之類的事件本身沒有事件冒泡機制,所以無法委託;
- mousemove、mouseout這樣的事件,雖然有事件冒泡,但是隻能不斷通過位置去計算定位,對效能消耗高,因此也是不適合於事件委託的;
五、使用委託的注意項(可以叫應用項)
- 只在必須的地方,使用事件委託,比如:ajax的區域性重新整理區域
- 儘量的減少繫結的層級,並且不在body元素上,進行繫結;(
事件委託的原理離不開DOM的查詢;而瀏覽器太多層級的查詢非常耗效能
) - 減少繫結的次數,如果可以,那麼把多個事件的繫結,合併到一次事件委託中去,由這個事件委託的回撥,來進行分發。
六、經典面試題
mouseeneter 和 mouseover 的區別?
inner.onmouseenter = function () {
console.log('inner enter');
};
outer.onmouseenter = function () {
console.log('outer enter');
};
inner.onmouseleave = function () {
console.log('inner leave');
};
outer.onmouseleave = function () {
console.log('outer leave');
};
複製程式碼
講解:
- over屬於滑過(覆蓋)事件,從父元素進入到子元素,屬於離開了父元素,會觸發父元素的out,觸發子元素的over;enter屬於進入,從父元素進入子元素,並不算離開父元素,不會觸發父元素的leave,觸發子元素的enter
- enter和leave阻止了事件的冒泡傳播,而over和out還存在冒泡傳播的
所以對於父元素巢狀子元素這種情況,使用OVER會發生很多不願意操作的事情,此時我們使用ENTER會更加簡單,操作方便,所以真實專案中ENTER的使用會比OVER多
此文章中未涉及到相容性,有時間再加8⃣️