一韧拒、事件流
1淹接、事件冒泡
IE事件流被稱為事件冒泡。例如在div元素中觸發(fā)click事件叛溢,其click事件會(huì)沿著DOM數(shù)一路向上塑悼,在經(jīng)過的每個(gè)節(jié)點(diǎn)上依次觸發(fā)直到documen對象。現(xiàn)代瀏覽器中的事件會(huì)一直冒泡到window對象楷掉,如圖所示厢蒜。
2、事件捕獲
Netscape Communication團(tuán)隊(duì)提出了事件捕獲的事件流烹植。而事件捕獲中斑鸦,同事件冒泡流區(qū)別在于其click事件是由document元素捕獲,然后沿DOM樹依次向下傳播直達(dá)目標(biāo)元素div草雕,如圖所示鄙才。
3、DOM事件流
按照規(guī)范事件流分為三個(gè)階段:事件捕獲促绵,達(dá)到目標(biāo)和事件冒泡。
1、事件捕獲:當(dāng)某個(gè)元素觸發(fā)某個(gè)事件败晴,頂層對象document就會(huì)發(fā)出一個(gè)事件流浓冒,隨著dom樹的節(jié)點(diǎn)向目標(biāo)元素節(jié)點(diǎn)流去,直到到達(dá)事件真正發(fā)生的目標(biāo)元素尖坤,在這個(gè)過程中稳懒,事件相應(yīng)的監(jiān)聽函數(shù)是不會(huì)被觸發(fā)的
2、事件目標(biāo):當(dāng)?shù)竭_(dá)目標(biāo)元素之后慢味,執(zhí)行目標(biāo)元素該事件相應(yīng)的處理函數(shù)场梆,如果沒有綁定監(jiān)聽函數(shù),那就不執(zhí)行
3纯路、事件冒泡:從目標(biāo)元素開始或油,往頂層元素傳播,途中如果有節(jié)點(diǎn)綁定了相應(yīng)的事件處理函數(shù)驰唬,這些函數(shù)都會(huì)被一次觸發(fā)顶岸,如果想阻止事件冒泡,可以使用event.stopPropgation()或者event.cancelBubble=true來阻止事件的冒泡傳播
4叫编、事件流的阻止
stopPropagation()
//既可以阻止事件冒泡辖佣,也可以阻止事件捕獲,也可以阻止處于目標(biāo)階段
stopImmediatePropagation()
//既可以阻止事件冒泡搓逾,也可以阻止事件捕獲卷谈,還會(huì)阻止該元素其他事件的發(fā)生
二、事件綁定和解綁
1霞篡、事件綁定
a世蔗、html中直接綁定——html中綁定事件叫做內(nèi)聯(lián)綁定事件,不利于分離
b寇损、js中直接綁定——js中直接綁定稱為賦值綁定函數(shù)凸郑,缺點(diǎn)是只能綁定一次
//事件監(jiān)聽,綁定
target.addEventListener(type, listener[, useCapture])
參數(shù)說明:
1矛市、target表示要監(jiān)聽事件的目標(biāo)對象芙沥,可以是一個(gè)文檔上的元素DOM本身,Window或者XMLHttpRequest
2浊吏、type表示事件類型的字符串
listener為當(dāng)指定的事件類型發(fā)生時(shí)被通知到的一個(gè)對象
useCapture為設(shè)置事件的捕獲或者冒泡
true為捕獲而昨,false為冒泡(默認(rèn))
3、addEventListener可以給同一個(gè)dom元素綁定多個(gè)函數(shù)找田,并且執(zhí)行順序按照綁定順序執(zhí)行歌憨,且執(zhí)行順序與useCapture無關(guān)
4、給一個(gè)dom元素綁定同一個(gè)函數(shù)墩衙,最多只能綁定useCapture類型不同的兩次
5务嫡、addEventListener只支持到IE9甲抖,為兼容性考慮,在兼容IE8及一下瀏覽器可以用attachEvent函數(shù)心铃,和addEventListener函數(shù)表現(xiàn)一樣准谚,但它綁定函數(shù)的this會(huì)指向全局
2、事件解綁
a去扣、通過dom的on***屬性設(shè)置的事件柱衔,可以用dom.onclick = null來解綁
b、通過addEventListener綁定的事件可以使用removeEventListener來解綁愉棱,接受參數(shù)一樣
c唆铐、對于使用removeEventListener函數(shù)解綁事件,需要傳入的listener奔滑,useCapture和addEventListener完全一致才可以解綁事件
三艾岂、事件委托和事件代理
1、事件委托和事件代理的理解
什么是事件委托档押?它還有一個(gè)名字叫事件代理澳盐,JavaScript高級程序設(shè)計(jì)上講:事件委托就是利用事件冒泡,只指定一個(gè)事件處理程序令宿,就可以管理某一類型的所有事件叼耙,當(dāng)我們需要對很多元素添加事件的時(shí)候,可以通過事件添加到他們的父節(jié)點(diǎn)二將時(shí)間委托給父節(jié)點(diǎn)來觸發(fā)處理函數(shù)粒没。
2筛婉、使用事件委托的優(yōu)點(diǎn)
一般來說,dom需要有事件處理程序癞松,我們都會(huì)直接給它設(shè)置事件處理程序就好了爽撒,那如果是很多的dom需要添加事件處理呢?比如我們這里有100個(gè)li响蓉,每個(gè)li都有相同的click事件硕勿,那么我們會(huì)用for循環(huán)的方法來遍歷所有的li,然后給他們添加事件枫甲,那么這樣會(huì)存在什么問題呢源武?
在JavaScript中,添加到頁面上的事件處理程序的數(shù)量將直接關(guān)聯(lián)到頁面整體的運(yùn)行性能想幻,因?yàn)樾枰粩嗟呐cdom節(jié)點(diǎn)進(jìn)行交互粱栖,訪問dom的次數(shù)越多,引起瀏覽器重繪與重排的次數(shù)就越多脏毯,就會(huì)延長整個(gè)頁面交互就緒時(shí)間闹究,這就是為什么性能優(yōu)化的主要思想是減少dom操作的原因,如果使用事件委托食店,就會(huì)將所有的操作放到j(luò)s程序里面渣淤,與dom的操作就只需要交互一次赏寇,這樣就能大大的減少與dom的交互次數(shù),提高性能砂代。
每個(gè)函數(shù)都是一個(gè)對象蹋订,是對象就會(huì)占用內(nèi)存,內(nèi)存占用率就越大刻伊,自然性能就差了,比如上面的100個(gè)li椒功,就要占用100個(gè)內(nèi)存空間捶箱,如果是1000個(gè),10000個(gè)呢动漾,如果使用事件委托丁屎,那么我們就可以只對它的父級這一個(gè)對象(如果只有一個(gè)父級)進(jìn)行操作,這樣我們就需要一個(gè)內(nèi)存空間就夠了旱眯,是不是省了很多晨川,自然性能就會(huì)更好。
3删豺、事件委托的原理實(shí)現(xiàn)
事件委托是利用事件的冒泡機(jī)制來實(shí)現(xiàn)的共虑,Event對象提供了一個(gè)屬性叫target,可以返回事件的目標(biāo)節(jié)點(diǎn)呀页,我們稱為事件源妈拌,也就是說,target就可以表示為當(dāng)前事件操作的dom蓬蝶,但是不是真正操作的dom尘分。標(biāo)準(zhǔn)瀏覽器用event.target,此時(shí)知識獲取了當(dāng)前節(jié)點(diǎn)的位置丸氛,并不知道是什么節(jié)點(diǎn)名稱培愁,這里我們用nodeName來獲取具體是什么標(biāo)簽名,這個(gè)返回的是一個(gè)大寫的缓窜,一般轉(zhuǎn)化為小寫再進(jìn)行比較
如果你想將事件委托給父元素來處理定续,但每個(gè)子元素的事件內(nèi)容又不相同時(shí),這里我們可以給每個(gè)子元素添加一個(gè)唯一的key來作標(biāo)識雹洗,然后在父元素中對其進(jìn)行分別的處理香罐,例如:
const list = document.querySelector('#list)
const lists = list.querySelector('#list > li')for(let i=0; i<lists.length; i++){
lists[i].dataset.key = 'list-' + i
}
list.addEventListener('click',function(e){
const event = e || window.event
const target = event.target || event.srcElement
if(target.nodeName.toLocaleLowerCase() === 'li'){
switch(target.dataset.key){
case 'list-1':
do something-1
break
case 'list-2':
do something-2
break
default:
do something-3
break }
}
})