事件代理的定義
對(duì)于事件委托或者說(shuō)事件代理,有這樣一段定義:
事件委托就是利用事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。
舉個(gè)例子來(lái)說(shuō)
首先我們創(chuàng)建一個(gè)擁有多個(gè)平行元素的列表
<ul id="parent-list">
<li id="post-1">Item 1</li>
<li id="post-2">Item 2</li>
<li id="post-3">Item 3</li>
<li id="post-4">Item 4</li>
<li id="post-5">Item 5</li>
<li id="post-6">Item 6</li>
</ul>
當(dāng)鼠標(biāo)移動(dòng)到li上或者點(diǎn)擊了li的時(shí)候,需要觸發(fā)相應(yīng)的事件,若果不使用事件代理,那么我們需要為每一個(gè)li都添加相應(yīng)的 onClick()或者 onMouseOver()事件,代碼如下:
function addListeners4Li(liNode) {
liNode.onclick = function clickHandler(){alert('Click')};
liNode.onmouseover = function mouseOverHandler(){alert('MouseOver')}
}
window.onload = function(){
var ulNode = document.getElementById("parent-list");
var liNodes = ulNode.getElementByTagName("Li"); //獲取所有l(wèi)i節(jié)點(diǎn)
for(var i=0, l = liNodes.length; i < l; i++){
addListeners4Li(liNodes[i]); //為每一個(gè)li節(jié)點(diǎn)添加監(jiān)聽
}
這種方法的缺點(diǎn)在于
- 由于在javascript中,每一個(gè)函數(shù)都是一個(gè)對(duì)象,是對(duì)象就會(huì)占用內(nèi)存,對(duì)象越多,占用的內(nèi)存也就越大,在上面的方法中,我們創(chuàng)建了6個(gè)
liNodes對(duì)象,對(duì)每一個(gè)對(duì)象進(jìn)行監(jiān)聽,但是如果li的數(shù)量特別龐大時(shí),這種方法其實(shí)是很占用內(nèi)存的 - 在上面的方法中,我們對(duì)每一個(gè)
liNodes對(duì)象都添加了監(jiān)聽,而在javascript中,添加到頁(yè)面中的事件處理程序數(shù)量將直接影響程頁(yè)面的整體性能,因?yàn)槭录幚沓绦蛟蕉?,與dom交互的次數(shù)就越多,引起瀏覽器重繪與重排的次數(shù)也就越多,從而拖慢頁(yè)面的就緒時(shí)間。 - 如果我們需要頻繁的刪除和添加
li元素,就需要在每一次添加時(shí)都為其綁定事件,過(guò)于繁瑣。
總結(jié)一下就是:
**1. 創(chuàng)建的js對(duì)象過(guò)多,占用內(nèi)存
- JS與DOM之間的關(guān)聯(lián)過(guò)多,影響性能,容易造成內(nèi)存泄漏
- 需要管理的函數(shù)過(guò)多,對(duì)于每個(gè)元素都要添加監(jiān)聽**
使用事件代理可以很好的解決這兩個(gè)問(wèn)題,但在我們介紹事件代理之前,先要說(shuō)一下事件的冒泡機(jī)制
事件的冒泡及捕獲
不同的瀏覽器對(duì)于事件的冒泡及捕獲有不同的處理方式,這里主要介紹W3C對(duì)DOM2.0定義的標(biāo)準(zhǔn)事件:
圖片.png
事件捕獲:當(dāng)某個(gè)元素觸發(fā)某個(gè)事件(如onclick),頂層對(duì)象document就會(huì)發(fā)出一個(gè)事件流,隨著DOM樹的節(jié)點(diǎn)向目標(biāo)元素節(jié)點(diǎn)流去,直到到達(dá)事件真正發(fā)生的目標(biāo)元素。在這個(gè)過(guò)程中,事件相應(yīng)的監(jiān)聽函數(shù)是不會(huì)被觸發(fā)的。
事件目標(biāo):當(dāng)?shù)竭_(dá)目標(biāo)元素之后,執(zhí)行目標(biāo)元素該事件相應(yīng)的處理函數(shù)。如果沒有綁定監(jiān)聽函數(shù),那就不執(zhí)行。
事件冒泡:從目標(biāo)元素開始,往頂層元素傳播。途中如果有節(jié)點(diǎn)綁定了相應(yīng)的事件處理函數(shù),這些函數(shù)都會(huì)被一次觸發(fā)。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來(lái)組織事件的冒泡傳播。
其中 addEventListener(eventType,callBack,true | false)函數(shù)的第三個(gè)屬性為 useCapture這個(gè)屬性默認(rèn)為 false意為在事件冒泡階段調(diào)用事件處理函數(shù),如果為 true則是在事件捕獲階段調(diào)用事件處理函數(shù)。
事件代理的使用
對(duì)于上面的代碼,我們可以使用事件代理來(lái)進(jìn)行簡(jiǎn)化,只對(duì)平行元素共同的父元素進(jìn)行監(jiān)聽,根據(jù)其獲取的源事件屬性,來(lái)做出相應(yīng)的處理:
document.getElementById("parent-list").addEventListener("click",function(e) {
//檢查事件源的屬性
if(e.target && e.target.nodeName.toUpperCase == "LI") {
console.log("List item ",e.target.id.replace("post-")," was clicked!");
}
}
在上述代碼中,在 click()事件的毀掉函數(shù)中有一個(gè)源事件 e,通過(guò)查看源事件中的屬性,我們可以確定當(dāng)前事件是從哪個(gè)元素觸發(fā)的,從而做出相應(yīng)的Actions
