一、事件流
1、事件冒泡
IE事件流被稱為事件冒泡。例如在div元素中觸發(fā)click事件,其click事件會沿著DOM數(shù)一路向上,在經(jīng)過的每個節(jié)點上依次觸發(fā)直到documen對象?,F(xiàn)代瀏覽器中的事件會一直冒泡到window對象,如圖所示。

2、事件捕獲
Netscape Communication團(tuán)隊提出了事件捕獲的事件流。而事件捕獲中,同事件冒泡流區(qū)別在于其click事件是由document元素捕獲,然后沿DOM樹依次向下傳播直達(dá)目標(biāo)元素div,如圖所示。

3、DOM事件流
按照規(guī)范事件流分為三個階段:事件捕獲,達(dá)到目標(biāo)和事件冒泡。
1、事件捕獲:當(dāng)某個元素觸發(fā)某個事件,頂層對象document就會發(fā)出一個事件流,隨著dom樹的節(jié)點向目標(biāo)元素節(jié)點流去,直到到達(dá)事件真正發(fā)生的目標(biāo)元素,在這個過程中,事件相應(yīng)的監(jiān)聽函數(shù)是不會被觸發(fā)的
2、事件目標(biāo):當(dāng)?shù)竭_(dá)目標(biāo)元素之后,執(zhí)行目標(biāo)元素該事件相應(yīng)的處理函數(shù),如果沒有綁定監(jiān)聽函數(shù),那就不執(zhí)行
3、事件冒泡:從目標(biāo)元素開始,往頂層元素傳播,途中如果有節(jié)點綁定了相應(yīng)的事件處理函數(shù),這些函數(shù)都會被一次觸發(fā),如果想阻止事件冒泡,可以使用event.stopPropgation()或者event.cancelBubble=true來阻止事件的冒泡傳播

4、事件流的阻止
stopPropagation()
//既可以阻止事件冒泡,也可以阻止事件捕獲,也可以阻止處于目標(biāo)階段
stopImmediatePropagation()
//既可以阻止事件冒泡,也可以阻止事件捕獲,還會阻止該元素其他事件的發(fā)生
二、事件綁定和解綁
1、事件綁定
a、html中直接綁定——html中綁定事件叫做內(nèi)聯(lián)綁定事件,不利于分離
b、js中直接綁定——js中直接綁定稱為賦值綁定函數(shù),缺點是只能綁定一次
//事件監(jiān)聽,綁定
target.addEventListener(type, listener[, useCapture])
參數(shù)說明:
1、target表示要監(jiān)聽事件的目標(biāo)對象,可以是一個文檔上的元素DOM本身,Window或者XMLHttpRequest
2、type表示事件類型的字符串
listener為當(dāng)指定的事件類型發(fā)生時被通知到的一個對象
useCapture為設(shè)置事件的捕獲或者冒泡
true為捕獲,false為冒泡(默認(rèn))
3、addEventListener可以給同一個dom元素綁定多個函數(shù),并且執(zhí)行順序按照綁定順序執(zhí)行,且執(zhí)行順序與useCapture無關(guān)
4、給一個dom元素綁定同一個函數(shù),最多只能綁定useCapture類型不同的兩次
5、addEventListener只支持到IE9,為兼容性考慮,在兼容IE8及一下瀏覽器可以用attachEvent函數(shù),和addEventListener函數(shù)表現(xiàn)一樣,但它綁定函數(shù)的this會指向全局
2、事件解綁
a、通過dom的on***屬性設(shè)置的事件,可以用dom.onclick = null來解綁
b、通過addEventListener綁定的事件可以使用removeEventListener來解綁,接受參數(shù)一樣
c、對于使用removeEventListener函數(shù)解綁事件,需要傳入的listener,useCapture和addEventListener完全一致才可以解綁事件
三、事件委托和事件代理
1、事件委托和事件代理的理解
什么是事件委托?它還有一個名字叫事件代理,JavaScript高級程序設(shè)計上講:事件委托就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件,當(dāng)我們需要對很多元素添加事件的時候,可以通過事件添加到他們的父節(jié)點二將時間委托給父節(jié)點來觸發(fā)處理函數(shù)。
2、使用事件委托的優(yōu)點
一般來說,dom需要有事件處理程序,我們都會直接給它設(shè)置事件處理程序就好了,那如果是很多的dom需要添加事件處理呢?比如我們這里有100個li,每個li都有相同的click事件,那么我們會用for循環(huán)的方法來遍歷所有的li,然后給他們添加事件,那么這樣會存在什么問題呢?
在JavaScript中,添加到頁面上的事件處理程序的數(shù)量將直接關(guān)聯(lián)到頁面整體的運(yùn)行性能,因為需要不斷的與dom節(jié)點進(jìn)行交互,訪問dom的次數(shù)越多,引起瀏覽器重繪與重排的次數(shù)就越多,就會延長整個頁面交互就緒時間,這就是為什么性能優(yōu)化的主要思想是減少dom操作的原因,如果使用事件委托,就會將所有的操作放到j(luò)s程序里面,與dom的操作就只需要交互一次,這樣就能大大的減少與dom的交互次數(shù),提高性能。
每個函數(shù)都是一個對象,是對象就會占用內(nèi)存,內(nèi)存占用率就越大,自然性能就差了,比如上面的100個li,就要占用100個內(nèi)存空間,如果是1000個,10000個呢,如果使用事件委托,那么我們就可以只對它的父級這一個對象(如果只有一個父級)進(jìn)行操作,這樣我們就需要一個內(nèi)存空間就夠了,是不是省了很多,自然性能就會更好。
3、事件委托的原理實現(xiàn)
事件委托是利用事件的冒泡機(jī)制來實現(xiàn)的,Event對象提供了一個屬性叫target,可以返回事件的目標(biāo)節(jié)點,我們稱為事件源,也就是說,target就可以表示為當(dāng)前事件操作的dom,但是不是真正操作的dom。標(biāo)準(zhǔn)瀏覽器用event.target,此時知識獲取了當(dāng)前節(jié)點的位置,并不知道是什么節(jié)點名稱,這里我們用nodeName來獲取具體是什么標(biāo)簽名,這個返回的是一個大寫的,一般轉(zhuǎn)化為小寫再進(jìn)行比較
如果你想將事件委托給父元素來處理,但每個子元素的事件內(nèi)容又不相同時,這里我們可以給每個子元素添加一個唯一的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 }
}
})