教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

什么是事件委托?JavaScript事件委托的實現(xiàn)原理

更新時間:2020年10月20日10時24分 來源:傳智播客 瀏覽次數(shù):

引言:

事件委托應(yīng)用在很多開發(fā)場景之中,但是很多同學(xué)對委托的原理、特別是對JS原生實現(xiàn)委托不太了解。每每看到此情此景我總覺得“眾生皆苦”,正所謂“我不寫文章,誰寫文章”的普渡心態(tài),是以提供這篇文章解救眾生之苦,阿彌陀佛!

釋義

在學(xué)事件委托時,我們有必要先對事件委托做一個定義。

JS里的事件委托:就是當(dāng)事件觸發(fā)時,把要做的事委托給父元素來處理。

再通俗點:就是自己的事不想干,叫它爸爸,甚至爺爺、甚至祖先來干。

作用

在學(xué)它的用法和原理之前,我們先了解一下它的作用,有什么好處。再讓各位決定是否愿意繼續(xù)看下去呢?

作用1:節(jié)約內(nèi)存(哇塞,這個好這個棒!)

作用2:能為之后新增的DOM元素依然添加事件(路人甲:這什么鬼?我:死相,真猴急。往后面看就知道了!)

揭開事件委托面紗

場景1:當(dāng)多個li標(biāo)簽需要添加點擊事件時

代碼如圖:

事件委托01


代碼解析:

給5個li標(biāo)簽加了點擊事件,當(dāng)界面上點擊li時,會打印它們各自li標(biāo)簽顯示的內(nèi)容。

出現(xiàn)的問題:

此時5個li,看起來每個li的點擊事件觸發(fā)時調(diào)用的都是同一個函數(shù),即:


function(){
    console.log(this.innerHTML);             
};

但其實并不是這樣。每個li綁定的都是一個全新的函數(shù),只不過每個函數(shù)的樣子都一毛一樣。

如何驗證這個結(jié)論呢?很簡單,判斷每個li標(biāo)簽的onclick是否相等就可以了

代碼驗證如下:

事件委托02


得到結(jié)果:

事件委托03


至于上面說的函數(shù)長得一樣,但不是同一個數(shù)據(jù)這種情況就類似于 var obj1 = {name:"jack",age:16}和var obj2 = {name:"jack",age:16},obj1 == obj2 得到的結(jié)果也會是false(如對這一塊不理解,下次有空再寫一篇文章解答。或者來黑馬程序員實地學(xué)習(xí),課程里有講)

至此,我們可以得到結(jié)論,如果有5個li,那么就有5個函數(shù)會被創(chuàng)建在內(nèi)存中占據(jù)空間,那如果有100個li呢?就會有100個長相一毛一樣的函數(shù)在內(nèi)存中常駐,對內(nèi)存的開銷是巨大的!

解決辦法:

利用事件冒泡的原理,把事件加在父元素(ul)身上!

代碼如下:

事件委托04


原理解析:

回顧事件冒泡

事件冒泡:即一個元素的事件觸發(fā)后,會依次一級一級往上調(diào)用父級元素的同名事件,直到window(注:IE8和之前的瀏覽器只到document)

例:div > p > span 當(dāng)div和p以及span都添加了click事件,當(dāng)點擊span時,會依次向上觸發(fā)span、p、div的click事件。

代碼如下:

事件委托05


效果如下:

事件委托06


其中,在每個觸發(fā)的事件里,通過事件對象.target能拿到 觸發(fā)事件的源頭元素 也就是 事件源。

因此,在上述代碼中,改成

事件委托07


可發(fā)現(xiàn),觸發(fā)事件時,打印出來的e.target都是span,如下:

事件委托08

注:事件對象在ie8中要通過window.event才能取到,因此e = e || window.event是做兼容處理(詳細了解請翻閱黑馬程序員前端課程關(guān)于事件對象的講解)

解釋委托原理

在回顧完事件冒泡后,我們顯而易見得到結(jié)論:給所有l(wèi)i添加點擊事件,只要加到它們的父元素ul身上的根本原因是利用了事件冒泡。也即:無論點擊哪個li,都會自動觸發(fā)ul的點擊事件,然后在ul里通過e.target能獲得真正被點擊的那個li,繼而拿到它的innerHTML

小結(jié):如果給一堆元素加事件,并且事件觸發(fā)時執(zhí)行的代碼都差不多時,就可以把事件加在父元素身上啦!這樣可以更節(jié)省內(nèi)存空間哦!O(∩_∩)O哈哈~(來自摳腳大漢的賣萌符號)

看,這樣的形式是不是就相當(dāng)于把自己的事件,委托在父元素身上處理了呢?因而它才會叫事件委托!

也就是:自己的活不干了,給它爹去干!(畫外音:一看就是坑爹的貨~)

存在問題及解決方式

思考:如果ul里還有其他子元素例如span,可我只想給li加點擊事件,用原來寫的事件委托還行嗎?

答案是否定的,因為根據(jù)事件冒泡原理,所有子元素點擊后都會觸發(fā)父元素的點擊,因此,如果你點擊了span,也會調(diào)用ul的點擊事件,這就相當(dāng)于給span也加了點擊事件。這時候該怎么解決呢?

很簡單,只要判斷一下事件源是不是li就行了,如果是li才執(zhí)行代碼,否則不執(zhí)行,代碼如下:

事件委托09

場景2: 新增元素沒有綁定事件的問題

界面描述:界面上有一個ul里面默認有5個li,并且還有一個按鈕,當(dāng)點擊按鈕就創(chuàng)建一個新的li,需要不管是默認有的li還是新的li都有點擊事件。

問題描述:

我們先嘗試不用事件委托普通寫法會怎么寫,代碼和界面如下:

事件委托
事件委托11


JS部分代碼如下:

此時會發(fā)現(xiàn):頁面剛開始加載時就默認存在的5個li是有點擊事件的,但是點擊按鈕創(chuàng)建出來的li沒有點擊事件。

原因剖析:

因為上面的JS代碼是在頁面剛加載時執(zhí)行的,在當(dāng)時因為不可能去點擊按鈕,所以能找到的li標(biāo)簽只有默認那5個,因此你打印liList,發(fā)現(xiàn)也只有5個

事件委托12


因此,你遍歷liList給每個元素加點擊事件時,只能給這5個li加到點擊事件。因此,如果后面再有l(wèi)i標(biāo)簽,也不擁有點擊事件。

使用事件委托解決

代碼如下:

事件委托13


把事件只加在ul身上,即可解決,這樣內(nèi)存占用更低,代碼也少了很多!效果如圖:

事件委托14


我們可以看到,不管是默認有的5個li還是后面才增加的li都有點擊事件了

解析:因為事件冒泡機制的存在,不管是原本有的li還是新創(chuàng)建的li,當(dāng)事件觸發(fā)時都會一級一級往上調(diào)用父元素的同名事件。因此,只要是點擊的li標(biāo)簽,都會觸發(fā)ul的點擊事件,所以只要把事件加在ul身上就解決了不管新舊li標(biāo)簽都有點擊事件的問題。

jQuery里的事件委托

眾所周知,jQuery是JS的一個偉大的第三方庫(什么?你還不知道?火星了吧!趕緊來黑馬程序員惡補!)。JS有的方法,jQuery里都有,并且代碼寫起來更短。因此事件委托,其實在jQuery里也存在!

jQuery事件委托語法:

$('父元素').on('事件名','哪個子元素觸發(fā)',傳給回調(diào)函數(shù)的參數(shù),事件觸發(fā)時的回調(diào)函數(shù));

解釋:

參數(shù)1:事件名,代表要綁定什么事件,但是記得不用加on,也就是說如果你想加點擊事件,只要寫'click'即可,注意是字符串!所以要打單引號或者雙引號

參數(shù)2:只能由哪個子元素觸發(fā),例如我寫 "li",就代表只能由這個父元素里面的li觸發(fā)事件,其他子元素不會觸發(fā)。需要注意的是,這也是字符串,并且,參數(shù)2可以不寫,那就代表僅僅只是給父元素加一個點擊事件,并且所有子元素都能觸發(fā)到這個事件(因為事件冒泡)

參數(shù)3:其實一般不會用,就是如果想事件觸發(fā)時,自己給回調(diào)函數(shù)傳一些值就寫,這個參數(shù)也可以不寫!

參數(shù)4:事件觸發(fā)時的回調(diào)函數(shù)

總結(jié):參數(shù)1和參數(shù)4是必須的,其他是可選的,如果你要用事件委托,請寫上參數(shù)2

例:

事件委托14


說明:

1.on這個方法的參數(shù)3可以通過回調(diào)函數(shù)里的e.data拿到(但一般不會用,大家了解一下有這么個東西即可)

2.在jQuery事件委托的回調(diào)函數(shù)里this和e.target是同一個東西,但是在JS里this和e.target不是同一個東西(有興趣可以參考以前的文章或者黑馬程序員前端課程)

一般參數(shù)3不會寫,因此代碼常見會寫成這樣:

事件委托15

結(jié)語

其實在其他語言里,事件委托會比較復(fù)雜,需要創(chuàng)建額外對象。但是由于JS的靈活性,使得在JS里僅僅只需要把事件綁定在父元素即可實現(xiàn)。

事件委托雖然在面試題中略微少見,但是在實際開發(fā)中幾乎都會用到。因為有時候需要給某一類元素加事件(例如給所有l(wèi)i加點擊事件),因為網(wǎng)頁內(nèi)容經(jīng)常改變,這類元素隨時會增加或者減少,為了保證所有這類元素都有事件,也為了節(jié)約內(nèi)存,所以都需要采用事件委托才可實現(xiàn)!

最后含著淚,摳著腳跟大伙say byebye,我們下期再見~!


猜你喜歡:

Js中深拷貝與淺拷貝的區(qū)別 

目前最好的js異步 

傳智播客web前端培訓(xùn)課程 

黑馬程序員web前端培訓(xùn)課程 

0 分享到:
和我們在線交談!