事件委託詳解最新版

大白的掘金發表於2019-03-12

一、事件傳播機制

想要深入瞭解委託,最好先深入理解事件傳播機制:

我們把事件分為三個階段:捕獲階段目標階段冒泡階段;

  1. 捕獲階段:當點選時,先經過捕獲階段,從最外層如html(這裡不考慮相容了)層層找到目標
  2. 目標階段:經過目標階段,響應事件
  3. 冒泡階段:從點選目標往外層層觸發相同的事件方法直到最外層(根節點)

觸發當前元素的某一個事件(點選事件)行為,不僅當前元素事件行為觸發,而且其祖先元素的相關事件行為也會依次被觸發,這種機制就是 “事件的冒泡傳播機制”

如圖:

事件委託詳解最新版

如果有不太理解的可以看這個小?哦?:

比如某一天你在河邊玩耍,,走著走著,看到一個小石子,哼,把小石子往河裡一扔,河就泛起了層層波浪,對不對啦。這個時候就叫做冒泡

二、事件栗子(冒泡)

栗子

<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>
複製程式碼

四、委託的侷限性

  1. 比如 focus、blur 之類的事件本身沒有事件冒泡機制,所以無法委託;
  2. mousemove、mouseout這樣的事件,雖然有事件冒泡,但是隻能不斷通過位置去計算定位,對效能消耗高,因此也是不適合於事件委託的;

五、使用委託的注意項(可以叫應用項)

  1. 只在必須的地方,使用事件委託,比如:ajax的區域性重新整理區域
  2. 儘量的減少繫結的層級,並且不在body元素上,進行繫結;(事件委託的原理離不開DOM的查詢;而瀏覽器太多層級的查詢非常耗效能
  3. 減少繫結的次數,如果可以,那麼把多個事件的繫結,合併到一次事件委託中去,由這個事件委託的回撥,來進行分發。

六、經典面試題

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');
};
複製程式碼

講解:

  1. over屬於滑過(覆蓋)事件,從父元素進入到子元素,屬於離開了父元素,會觸發父元素的out,觸發子元素的over;enter屬於進入,從父元素進入子元素,並不算離開父元素,不會觸發父元素的leave,觸發子元素的enter
  2. enter和leave阻止了事件的冒泡傳播,而over和out還存在冒泡傳播的

所以對於父元素巢狀子元素這種情況,使用OVER會發生很多不願意操作的事情,此時我們使用ENTER會更加簡單,操作方便,所以真實專案中ENTER的使用會比OVER多

此文章中未涉及到相容性,有時間再加8⃣️

相關文章