1 什么是事件委托?
用個(gè)例子就可以很簡(jiǎn)單的解釋事件委托是怎么一回事了:
假設(shè)一個(gè)公司有三個(gè)職員在網(wǎng)上買了東西,那么他們?nèi)齻€(gè)在接收快遞的時(shí)候有兩種方法并炮。
第一種:等快遞來的時(shí)候,自己下樓去拿郭怪。
第二種:快遞來的時(shí)候,先經(jīng)過公司前臺(tái),然后在分發(fā)給每個(gè)人卿闹。
然而,正常情況下,第一種顯然比較浪費(fèi)勞動(dòng)力,所以現(xiàn)實(shí)情況我們都采取第二種方法。
而且,第二種方法有個(gè)優(yōu)勢(shì),新加入的職員也可以享受代簽快遞的服務(wù)八毯。
我們把場(chǎng)景換到網(wǎng)頁(yè)上:
假設(shè)一個(gè)網(wǎng)頁(yè)中有三個(gè)li,并且綁定了事件,那么它們?nèi)齻€(gè)在觸發(fā)事件的時(shí)候有兩種方法袜炕。
第一種:當(dāng)用戶點(diǎn)擊了li的時(shí)候,li觸發(fā)事件
第二種:當(dāng)用戶點(diǎn)擊了ul的時(shí)候,ul中的li觸發(fā)事件
優(yōu)勢(shì),顯而易見就是新加入的li也可以通過ul觸發(fā)锭吨。
也就是說事件委托,就是把本該由li觸發(fā)的事件,交由ul代理。
2 為什么要用事件委托?
道理我都懂,可為什么要交給ul委托呢?我把每個(gè)li都綁定一遍不好嗎?
一般來說傲醉,dom需要有事件處理程序蝇闭,我們都會(huì)直接給它設(shè)事件處理程序就好了,那如果是很多的dom需要添加事件處理呢硬毕?比如我們有100個(gè)li呻引,每個(gè)li都有相同的click點(diǎn)擊事件,可能我們會(huì)用for循環(huán)的方法吐咳,來遍歷所有的li逻悠,然后給它們添加事件,那這么做會(huì)存在什么影響呢韭脊?
在JavaScript中童谒,添加到頁(yè)面上的事件處理程序數(shù)量將直接關(guān)系到頁(yè)面的整體運(yùn)行性能,因?yàn)樾枰粩嗟呐cdom節(jié)點(diǎn)進(jìn)行交互沪羔,訪問dom的次數(shù)越多饥伊,引起瀏覽器重繪與重排的次數(shù)也就越多,就會(huì)延長(zhǎng)整個(gè)頁(yè)面的交互就緒時(shí)間任内,這就是為什么性能優(yōu)化的主要思想之一就是減少DOM操作的原因撵渡;如果要用事件委托,就會(huì)將所有的操作放到j(luò)s程序里面死嗦,與dom的操作就只需要交互一次趋距,這樣就能大大的減少與dom的交互次數(shù),提高性能越除;
每個(gè)函數(shù)都是一個(gè)對(duì)象节腐,是對(duì)象就會(huì)占用內(nèi)存外盯,對(duì)象越多,內(nèi)存占用率就越大翼雀,自然性能就越差了(內(nèi)存不夠用饱苟,是硬傷,哈哈)狼渊,比如上面的100個(gè)li箱熬,就要占用100個(gè)內(nèi)存空間,如果是1000個(gè)狈邑,10000個(gè)呢城须,那只能說呵呵了,如果用事件委托米苹,那么我們就可以只對(duì)它的父級(jí)(如果只有一個(gè)父級(jí))這一個(gè)對(duì)象進(jìn)行操作糕伐,這樣我們就需要一個(gè)內(nèi)存空間就夠了,是不是省了很多蘸嘶,自然性能就會(huì)更好良瞧。
3 事件委托的原理
冒泡
事件委托是利用事件的冒泡原理來實(shí)現(xiàn)的,何為事件冒泡呢训唱?舉個(gè)例子:
-
先創(chuàng)建三個(gè)不同顏色的div,X>Y>Z
我們先創(chuàng)建三個(gè)div -
然后分別給XYZ綁定事件
綁定事件 -
然后就可以測(cè)試冒泡事件了,我們先點(diǎn)擊一下最里面的黃色div,發(fā)現(xiàn)三個(gè)事件都觸發(fā)了
三個(gè)事件都觸發(fā)了 -
我們?cè)冱c(diǎn)擊一下綠色div,發(fā)現(xiàn)只觸發(fā)了綠色div和紅色div的事件.
只觸發(fā)了綠色div和紅色div的事件 - 所以我們可以總結(jié),事件冒泡的規(guī)則就是事件從最深的節(jié)點(diǎn)開始褥蚯,然后逐步向上傳播事件。同時(shí)這也是事件委托雪情,委托它們父級(jí)代為執(zhí)行事件遵岩。
4. 如何實(shí)現(xiàn)委托?
既然是委托,那么直接給li的父級(jí)ul綁定事件就可以了啊。但是這里存在一個(gè)BUG!
我們換個(gè)例子:
ul下有4個(gè)li,并且給ul綁定事件,為了方便演示給ul和li加一個(gè)border
BUG就是當(dāng)我們點(diǎn)擊紅色和綠色之間的時(shí)候也會(huì)觸發(fā)事件!!!
那么這個(gè)BUG如何處理呢?
如果在用戶點(diǎn)擊的時(shí)候判斷一下是不是點(diǎn)擊了li巡通,如果是就觸發(fā)尘执,如果不是就不觸發(fā),是不是就解決了?
這樣看似成功了,只有點(diǎn)擊了li才會(huì)觸發(fā)事件.
但是并不完美,因?yàn)楫?dāng)li有子元素的時(shí)候就會(huì)失效!!!
那么我們?cè)撊绾卫^續(xù)優(yōu)化呢?
當(dāng)我們發(fā)現(xiàn)被點(diǎn)擊的元素不是li的時(shí)候,那么我們就找被點(diǎn)擊元素的父元素,判斷它是不是li,如果不是就繼續(xù)找.......以此類推,當(dāng)我們找到li那么就觸發(fā)事件,如果沒找到,就不觸發(fā)宴凉。
最終代碼:
var ul = document.querySelector('.a')
ul.addEventListener('click', function (e) {
var 被點(diǎn)擊的元素 = e.target
while (被點(diǎn)擊的元素.tagName !== 'LI') {
被點(diǎn)擊的元素 = 被點(diǎn)擊的元素.parentNode
if (被點(diǎn)擊的元素 === ul) {
被點(diǎn)擊的元素 = null
break;
}
}
if (被點(diǎn)擊的元素) {
console.log('觸發(fā)成功')
}
}) ```