使用原生JS實(shí)現(xiàn)事件委托

事件委托(Event Delegation)是JS中一項(xiàng)十分重要的應(yīng)用,使用事件委托可以避免對(duì)每一個(gè)節(jié)點(diǎn)添加監(jiān)聽(tīng)器慌随,只要把監(jiān)聽(tīng)器加到它們的父元素上就可以利用冒泡階段來(lái)監(jiān)聽(tīng)子元素的事件垛贤。接下來(lái)我們通過(guò)一段代碼來(lái)理解事件委托的過(guò)程和原理焰坪。

HTML代碼如下:

<style>
    li{
      border:1px solid blue;
      text-align:center;
    }
    ul{
      background-color:rgba(255,0,0,0.5);
      padding:20px;
    }
  </style>
  <ul>
    <li>小明</li>
    <li>小華</li>
    <li>小強(qiáng)</li>
    <li>小胖</li>
    <li>小紅</li>
  </ul>

在沒(méi)有添加JS代碼的情況下顯示出來(lái)的效果:

沒(méi)有添加JS代碼

現(xiàn)在要添加JS代碼,從最基礎(chǔ)的來(lái):

var list = document.querySelectorAll('li')

function fn(){
  console.log('OK')
}

list[0].addEventListener('click',fn)
list[1].addEventListener('click',fn)
list[2].addEventListener('click',fn)
list[3].addEventListener('click',fn)
list[4].addEventListener('click',fn)

這段代碼的思路就是聘惦,先用document.querySelectorAll()方法訪問(wèn)到所有的li節(jié)點(diǎn)某饰,并且把這些標(biāo)簽儲(chǔ)存在一個(gè)數(shù)組里,然后訪問(wèn)每個(gè)具體的節(jié)點(diǎn)并且用addEventListener給所有的li節(jié)點(diǎn)添加監(jiān)聽(tīng)器,當(dāng)鼠標(biāo)點(diǎn)擊各個(gè)li時(shí)黔漂,會(huì)觸發(fā)fn函數(shù)并且打出OK字符串诫尽。

但是這樣真的很麻煩呀,而且要有很多重復(fù)的代碼炬守,那么我們可以把JS代碼改成這樣:

var list = document.querySelectorAll('li')

function fn(){
  console.log('OK')
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)

這一串代碼的意思就是說(shuō)牧嫉,我監(jiān)聽(tīng)了li的父級(jí)元素,如果你點(diǎn)擊了ul就會(huì)觸發(fā)fn函數(shù)减途;同時(shí)酣藻,即使你點(diǎn)擊了li的內(nèi)容,因?yàn)閘i是ul的子無(wú)素鳍置,在冒泡階段辽剧,fn函數(shù)依然會(huì)被觸發(fā)。

但是這里有一個(gè)問(wèn)題税产,我想只點(diǎn)擊那幾個(gè)li不點(diǎn)擊ul就能觸發(fā)事件怕轿,就像最早的那個(gè)代碼一樣,那我們就可以在fn函數(shù)里加點(diǎn)條件砖第。JS代碼修改如下:

var list = document.querySelectorAll('li')

function fn(e){
  if(e.target.tagName ==='LI')
  console.log('OK')
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)

相對(duì)于之前的代碼撤卢,這個(gè)代碼在fn函數(shù)里增加了一件if條件來(lái)篩選,使用瀏覽器默認(rèn)e參數(shù)梧兼,如果e.target(你點(diǎn)擊的元素)的tagName是li的話,就打出OK字符串智听,如果不是的話就不打出來(lái)羽杰,這樣的效果就和之前那個(gè)麻煩的代碼一樣了,而且還精簡(jiǎn)了一些到推。注意:tagName的返回值是大寫的考赛,之前被坑過(guò)。

但是寫到這里又有新問(wèn)題出現(xiàn)了233333莉测,如果我點(diǎn)擊的HTML代碼里li元素里還有其它的子元素呢怎么辦呢颜骤?比如說(shuō)在第一個(gè)li元素里有一個(gè)span標(biāo)簽?新的HTML的代碼如下:

<style>
    li{
      border:1px solid blue;
      text-align:center;
    }
    ul{
      background-color:rgba(255,0,0,0.5);
      padding:20px;
    }
  </style>
  <ul>
    <li><span>小明</span></li>
    <li>小華</li>
    <li>小強(qiáng)</li>
    <li>小胖</li>
    <li>小紅</li>
  </ul>

這一部分的HTML/CSS代碼和之前的有差異捣卤,在第一個(gè)li里放入了一個(gè)span標(biāo)簽把小明又往里包了一層忍抽。如果按照上面JS代碼進(jìn)行運(yùn)行的話,因?yàn)閕f條件判斷句限制了當(dāng)前點(diǎn)擊的元素必須是li所以當(dāng)我點(diǎn)擊了處于span標(biāo)簽里的小明董朝,事件就監(jiān)聽(tīng)不到了鸠项。

哎~??????我聽(tīng)見(jiàn)了心碎的聲音233333。寫到這里子姜,還得繼續(xù)想辦法祟绊,既然我點(diǎn)擊了span里面的小明無(wú)法觸發(fā)事件,但是包住小明的span標(biāo)簽的爸爸是li啊,它是可以觸發(fā)的啊牧抽,那是不是只要我想辦法讓點(diǎn)擊的元素是li或者它的爸爸是li不就行了嗎嘉熊???好扬舒,我們繼續(xù)寫记舆,新的JS代碼如下:

var list = document.querySelectorAll('li')

function fn(e){
  if(e.target.tagName ==='LI' || e.target.parentNode.tagName ==='LI') 
  console.log('OK')
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)

這一段JS代碼相對(duì)于上面的變化的地方在于fn函數(shù)的if條件句中額外增加了一個(gè)‘或者’的邏輯運(yùn)算符,意思就是說(shuō)呼巴,如果我點(diǎn)擊的元素是li或者我點(diǎn)擊的元素的爸爸是li的話都可以打出OK字符串泽腮。這樣的話問(wèn)題就算是解決了。

但是衣赶,怕就怕在這個(gè)但是≌锷蓿現(xiàn)在的小明處于span標(biāo)簽里,span標(biāo)簽只有爸爸(li標(biāo)簽)府瞄,但是如果span還有爺爺呢碧磅,還有爺爺?shù)陌职趾蜖敔數(shù)臓敔斈兀窟@怎么辦遵馆,總不能給span的所有祖先都增加一個(gè)或者條件吧鲸郊?這還不坑死?那就只能繼續(xù)改:

var list = document.querySelectorAll('li')

function fn(e){
  var el = e.target
  while(el.tagName !== 'LI'){
    el = el.parentNode
  }
  console.log(el)
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)

改完以后的代碼就稍等有點(diǎn)繞了货邓。這里的代碼相對(duì)于前面的代碼它添加了一個(gè)while循環(huán)秆撮。大致思路就是,我先聲明一個(gè)變量el换况,然后把點(diǎn)擊的元素(e.target)賦值給el职辨,然后啟用一個(gè)while循環(huán),當(dāng)el的tagName不是li的時(shí)候戈二,就把el的父級(jí)元素重新賦值給el再進(jìn)行檢查舒裤,只要el的tagName是li了,那就跳出這個(gè)循環(huán)觉吭,執(zhí)行console.log(el)把el打出來(lái)腾供。

好像是皆大歡喜了,可是鲜滩,可是伴鳖。。绒北。新的JS代碼是基于一個(gè)基本前提的:那就是你點(diǎn)擊的元素往上找祖先元素是一定可以找到li的黎侈,但是在實(shí)際操作中,我們所點(diǎn)擊的元素的祖先元素是極有可能不是li的闷游,那怎么辦峻汉?總不能一直在while循環(huán)里跑吧贴汪,即使在while循環(huán)里跑,總會(huì)跑到body和document的休吠,再往上好像就返回null了扳埂。

為了模擬這種情況,我們就要改一下HTML/CSS代碼了瘤礁。

<style>
    li{
      border:1px solid blue;
      text-align:center;
    }
    ul{
      background-color:rgba(255,0,0,0.5);
      padding:20px;
    }
    div{
      background-color:green;
    }
  </style>
  
  <ul>
    <li><span>小明</span></li>
    <li>小華</li>
    <li>小強(qiáng)</li>
    <li>小胖</li>
    <li>小紅</li>
  </ul>
  <div><span>班長(zhǎng)大人</span></div>

頁(yè)面效果如下:

新的HTML代碼

新的HTML代碼增加了一個(gè)div阳懂,里面有一個(gè)兒子元素span。如果按照最新的邏輯寫出來(lái)的JS代碼如下:

var list = document.querySelectorAll('li')

function fn(e){
  var el = e.target
  while(el.tagName !== 'LI'){
    el = el.parentNode
    console.log(el)
  }
  console.log(el)
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)
var div = document.querySelector('div')
div.addEventListener('click',fn)

但是在點(diǎn)擊了班長(zhǎng)大人以后在瀏覽器里運(yùn)行的結(jié)果就悲劇了:

悲劇的結(jié)果

瀏覽器最后返回了一個(gè)null柜思,為啥呀岩调?因?yàn)樵赿iv里壓根就沒(méi)有什么li元素,所以按照代碼赡盘,瀏覽器一直往上找找到了document都沒(méi)有找到就只好返回一個(gè)null了号枕。有點(diǎn)冏冏冏~

所以還要繼續(xù)改JS代碼:

var list = document.querySelectorAll('li')

function fn(e){
  var el = e.target
  while(el.tagName !== 'LI'){
    el = el.parentNode
    if(el.tagName === 'BODY'){
      el = null
      break;
    }
  }
  if(el){
    console.log('你點(diǎn)擊了li')
  }else{
    console.log('你沒(méi)有點(diǎn)擊li')
  }
}

var ul = document.querySelector('ul')
ul.addEventListener('click',fn)
var div = document.querySelector('div')
div.addEventListener('click',fn)

這部分JS代碼的意思就是說(shuō),如果你往上找到了body元素那就得了別再找往上找了陨享,直接把el置空然后跳出循環(huán)吧葱淳。

寫到這里,終于寫完了抛姑,也寫不動(dòng)了赞厕。最后來(lái)梳理一下這個(gè)JS代碼吧。先看最下面的兩段定硝,分別取到了ul和div皿桑,再對(duì)它們進(jìn)行監(jiān)聽(tīng)。當(dāng)這ul和div以及它們的子元素被點(diǎn)擊時(shí)(冒泡階段)喷斋,就會(huì)觸發(fā)fn函數(shù)唁毒。

接下來(lái)看fn函數(shù)中的內(nèi)容。先聲明一個(gè)el星爪,然后把當(dāng)前點(diǎn)擊的元素賦值給它,在接下來(lái)的while循環(huán)中粉私,如果el的tagName不是li就找到它的爸爸再把它給el顽腾,如果它的爸爸是body的話就直接置空然后跳出循環(huán)。

以上的內(nèi)容為饑人谷直播十一班第二十一課事件委托部分的筆記诺核,我實(shí)際上就把它用自己的話再講一遍而已抄肖。

完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窖杀,一起剝皮案震驚了整個(gè)濱河市漓摩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌入客,老刑警劉巖管毙,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腿椎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡夭咬,警方通過(guò)查閱死者的電腦和手機(jī)啃炸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卓舵,“玉大人南用,你說(shuō)我怎么就攤上這事√屯澹” “怎么了裹虫?”我有些...
    開(kāi)封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)融击。 經(jīng)常有香客問(wèn)我筑公,道長(zhǎng),這世上最難降的妖魔是什么砚嘴? 我笑而不...
    開(kāi)封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任十酣,我火速辦了婚禮,結(jié)果婚禮上际长,老公的妹妹穿的比我還像新娘耸采。我一直安慰自己,他們只是感情好工育,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布虾宇。 她就那樣靜靜地躺著,像睡著了一般如绸。 火紅的嫁衣襯著肌膚如雪嘱朽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天怔接,我揣著相機(jī)與錄音搪泳,去河邊找鬼。 笑死扼脐,一個(gè)胖子當(dāng)著我的面吹牛岸军,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓦侮,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼艰赞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了肚吏?” 一聲冷哼從身側(cè)響起方妖,我...
    開(kāi)封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎罚攀,沒(méi)想到半個(gè)月后党觅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雌澄,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年仔役,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掷伙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡又兵,死狀恐怖任柜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沛厨,我是刑警寧澤宙地,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逆皮,受9級(jí)特大地震影響宅粥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜电谣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一秽梅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧剿牺,春花似錦企垦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至湃崩,卻和暖如春荧降,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背攒读。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工朵诫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薄扁。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓拗窃,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親泌辫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • DOM事件 在我們的日常的生活中九默,無(wú)時(shí)無(wú)刻不在發(fā)生著各種類型的事件震放。比如說(shuō)體育賽事、娛樂(lè)新聞驼修、戰(zhàn)爭(zhēng)甚至天氣變化等等...
    七里之境閱讀 7,492評(píng)論 1 3
  • JavaScript與HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的殿遂。在學(xué)習(xí)事件委托之前诈铛,我們需要先了解事件含義、事件綁定墨礁、事...
    田田kyle閱讀 385評(píng)論 0 0
  • 1幢竹、如何監(jiān)聽(tīng)事件 目前W3C對(duì)DOM進(jìn)行標(biāo)準(zhǔn)化規(guī)定中對(duì)事件監(jiān)聽(tīng)有兩種方式,DOM level 0 中規(guī)定 butt...
    字母31閱讀 402評(píng)論 0 0
  • 事件 在使用JavaScript與DOM交互時(shí),事件是用到的比較多的. JavaScript的事件機(jī)制是一個(gè)標(biāo)準(zhǔn)的...
    從這到那閱讀 708評(píng)論 0 1
  • 零售領(lǐng)域更像是一個(gè)戰(zhàn)場(chǎng)恩静,沃爾瑪很清除自己和對(duì)手在數(shù)位能力上的差距焕毫,試圖通過(guò)并購(gòu)來(lái)彌補(bǔ)。這會(huì)幫助它成功轉(zhuǎn)型嗎驶乾? 本文...
    葛朗臺(tái)商業(yè)簡(jiǎn)訊閱讀 174評(píng)論 0 0