javascript 中的事件委托詳解
這幾天看到一個面試題,大概就是,讓你給1000個li都添加一個click事件,應(yīng)該怎么添加?大多數(shù)人第一開始的感覺可能就是,每個li上邊都添加一個唄,那要是這樣的話,估計面試的時候就會GG了,這里就是撤出了我們的事件冒泡和捕獲機(jī)制,以及事件委托機(jī)制,對于上邊這些,我們慢慢來看.
首先說一下事件冒泡和事件捕獲機(jī)制,事件冒泡是有微軟公司提出來的,事件捕獲是有網(wǎng)景公司提出來的,當(dāng)時兩家是爭論的不可開交,后來w3c也沒辦法,就采取了折中的方式,事件產(chǎn)生后先捕獲后冒泡,
通常,在js中監(jiān)聽事件的方法共有三種,分別是:
ele.addEventListener(type,listener,[useCapture]);//IE6~8不支持
ele.attachEvent('on'+type,listener);//IE6~10支持,IE11不支持
ele.onClick=function(){};//所有瀏覽器都支持
w3c規(guī)范中定義了三個事件階段,依次是捕獲階段,目標(biāo)階段,冒泡階段,而w3c指定的dom2級規(guī)定中,使用的是addEventListener來監(jiān)聽事件的.所以我們就以addEventListener來講解,首先事假冒泡就像你從往水中扔一塊石子,水中的氣泡從下邊往上冒一樣,意思為觸發(fā)事件后從子元素王父元素方向觸發(fā),而捕獲機(jī)制則正好相反,捕獲機(jī)制是從父元素往子元素方向進(jìn)行事件觸發(fā),而addEventListener函數(shù)中的第三位參數(shù)正是來決定是使用捕獲機(jī)制還是冒泡機(jī)制的,當(dāng)useCapture為true是為捕獲機(jī)制,當(dāng)useCapture為false時是冒泡機(jī)制,我們看一下例子:
復(fù)制代碼
<div class="parent"> <div class="child"> </div> </div> <script> var parent = document.getElementsByClassName('parent')[0]; var child = document.getElementsByClassName('child')[0]; parent.addEventListener('click',function(){ console.log("這里是父元素"); },false); child.addEventListener('click',function(){ console.log("這里是子元素"); },false); </script>
當(dāng)我們點(diǎn)擊子元素是顯示上圖,當(dāng)我們將false改為true后就會發(fā)現(xiàn)執(zhí)行順序會反過來,這就是事件冒泡和捕獲的區(qū)別,他們兩個剛好相反,
那么使用這種綁定機(jī)制我們的弊端在于要去給每一個對象綁定事件會是一個特別麻煩的事情,當(dāng)我們要刪除一個事件或者要改變一個事件的時候會特別的繁瑣,更重要的是,我們增加了JavaScript和dom節(jié)點(diǎn)之間的關(guān)聯(lián),而且一點(diǎn)出現(xiàn)循環(huán)引用,很有可能造成內(nèi)存泄露,這些都是它的弊端,
那么解決這種弊端的一種方法就是事件代理(event delegation),這個方法可以讓你避免去給每一個節(jié)點(diǎn)一一的添加事件,它的做法是將這些監(jiān)聽事件去綁定到這些節(jié)點(diǎn)的父元素上,在父元素上的這個監(jiān)聽函數(shù)自動去判斷是哪一個子元素觸發(fā)的事件,從而可以對觸發(fā)事件的子元素進(jìn)行操作,這里我們給出的例子是davidwalsh所給出的一個例子:
現(xiàn)在我們有一個父元素ul和幾個li子元素,
<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>
現(xiàn)在我們要實(shí)現(xiàn)的是,當(dāng)我們點(diǎn)擊每一個li節(jié)點(diǎn)的時候,都會輸出li節(jié)點(diǎn)中的內(nèi)容,按照上邊的寫法,你可以選中這些li,讓后給他們加上這些方法,然后等到不需要了再將他們移除,如果有100個li,1000個li呢,這將會成為你的噩夢,較好的解決方法就是給父元素添加一個監(jiān)聽事件,之后的問題便是怎么去判斷出來時哪一個li被點(diǎn)擊了? 我們可以在監(jiān)聽事件中去判斷當(dāng)前event的target來判斷是否是我們要找的節(jié)點(diǎn),這里我們有一個簡單的例子:
// 找到父元素,綁定一個監(jiān)聽事件 document.getElementById("parent-list").addEventListener("click", function(e) { // e.target是點(diǎn)擊的元素 // 如果它是li元素 if(e.target && e.target.nodeName == "LI") { // console.log("List item ", e.target.id.replace("post-", ""), " was clicked!"); } });
當(dāng)ul中發(fā)生點(diǎn)擊事件后,因?yàn)閍ddEventListener默認(rèn)是冒泡事件,所以監(jiān)聽事件會在底層事件冒泡過來時執(zhí)行,在觸發(fā)了事件后,去檢測是否是我們要尋找的目標(biāo)元素,如果不是,就會忽略過去,那我們不僅僅可以通過目標(biāo)元素的標(biāo)簽是不是我們需要的目標(biāo)元素,我們還可以根據(jù)目標(biāo)元素的屬性或者類名來進(jìn)行檢測,利用ele.maeches這個API來進(jìn)行處理,
document.getElementById("myDiv").addEventListener("click",function(e) { // e.target 就是當(dāng)前被點(diǎn)擊的元素 if (e.target && e.target.matches("a.classA")) { console.log("Anchor element clicked!"); } });
因此我們可以看得出來,使用事件代理這種方式,能夠給我們帶來很多的便捷,可以避免很多坑,使用事件代理是一種很強(qiáng)大的方法.
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
javascript學(xué)習(xí)筆記_淺談基礎(chǔ)語法,類型,變量
下面小編就為大家?guī)硪黄猨avascript學(xué)習(xí)筆記_淺談基礎(chǔ)語法,類型,變量。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09Javascript標(biāo)準(zhǔn)DOM Range操作全集
Javascript標(biāo)準(zhǔn)DOM Range操作全集...2007-01-01關(guān)于js中alert彈出窗口文本換行問題簡單詳細(xì)說明
js中alert彈出窗口文本換行是一個小問題,本人很是郁悶,于是搜集整理下,曬出來和大家分享2012-12-12Javascript學(xué)習(xí)筆記之 對象篇(四) : for in 循環(huán)
如同 in 運(yùn)算符一樣,使用 for in 循環(huán)遍歷對象屬性時,也將往上遍歷整個原型鏈。2014-06-06JavaScript高級程序設(shè)計(第3版)學(xué)習(xí)筆記4 js運(yùn)算符和操作符
如果說數(shù)據(jù)類型是編程語言的磚瓦,那么運(yùn)算符和操作符則是編程語言的石灰和水泥了,它是將各種數(shù)據(jù)類型的值有機(jī)組合的糅合劑,使得數(shù)據(jù)值不再只是一個孤立的值,而有了一種動態(tài)的靈性2012-10-10nodejs中exports與module.exports的區(qū)別詳細(xì)介紹
你肯定非常熟悉nodejs模塊中的exports對象,你可以用它創(chuàng)建你的模塊接下來介紹創(chuàng)建過程,感興趣的朋友可以參考下2013-01-01