JavaScript與HTML之間的交互是通過事件實現的。在學習事件委托之前,我們需要先了解事件綁定、事件監聽、事件派發。
事件綁定
要使JS對用戶操作做出響應,第一步就需要給DOM元素綁定相應的事件函數。事件綁定有三種方法:
<ul id='list' onclik='event()'>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
1.直接在DOM元素上綁定
function event(){
console.log('給ul綁定成功')
}
2.DOM level 0:在JS代碼里添加
var ul = document.getElementById('list')
ul.onclick = function (){
console.log('給ul綁定成功')
}
3.DOM level 2:添加事件委托
前兩種方法在一個元素有多個事件需要執行的時候只執行最后綁定的事件,既最后一個事件會覆蓋前面的事件。另外,這需要給每一個li
寫一個監聽器,這樣既麻煩又占內存。所以我們我們推薦使用第三種方法。
事件監聽
事件監聽可以解決事件覆蓋的問題,我們用addEventListener()來實現監聽事件
//添加事件函數
function event1(){
console.log('給ul綁定成功1')
}
function event2(){
console.log('給ul綁定成功2')
}
//給相應的事件添加監聽器
var ul =document.querySelector('#list')
ul.addEventListener('click',event1)
ul.addEventListener("mouseover", event2);
事件監聽還有一個優點是它可以控制listener 的觸發階段。(即可以選擇捕獲或者冒泡)。接下來我們了解一下什么是捕獲階段和冒泡階段
事件派發
DOM內的事件傳播(或事件派發)總是沿著其文檔節點和其附元素所構成的有序列表進行的。
-
捕獲階段(Event capturing)
事件捕獲的思想是不太具體的節點應該更早接收到事件,而具體的節點應該最后接收到事件。事件捕獲的用意在于在事件到達預定目標之前捕獲它。
簡單一點解釋事件捕獲就是當事件觸發時先通知parent,再通知child 。
-
冒泡階段(Event Bubbling)
事件開始時由最具體的元素(文檔嵌套層次最深的那個節點)接收,然后逐級向上傳播到較為不具體的節點(文檔)。
簡單一點解釋事件冒泡就是當事件觸發時先通知child,再通知parent。
下圖展現了兩種事件通知順序:
注:我們可以使用
stopPropagation()
來阻止冒泡事件
另外上一節提到的addEventListener()
可以控制事件順序的優點,既在addEventListener()
里添加一個參數false
(執行冒泡)或者true
(執行捕獲),addEventListener()
默認為false
事件委托
事件委托基于以上三個知識點,下面我們來舉一個例子
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
var list = document.querySelector('#list')
list.addEventListener('click',function (e){
if(e.target.tagName === 'LI'){
console.log('當前元素事件觸發成功')
}
})
這是常規的實現事件委托的方法,但是這種方法有BUG,當監聽的元素里存在子元素時,那么我們點擊這個子元素事件會失效,所以我們可以聯系文章上一小節說到的冒泡事件傳播機制來解決這個bug。改進的事件委托代碼:
var ul = document.querySelector('#list')
list.addEventListener('click',function (e){
var l = e.target
//從target(點擊)元素向上找currentTarget(監聽)元素,找到了想委托的元素就觸發事件,沒找到就返回null
while(l.tagName !== 'LI'){
l = l.parentNode
if(l === ul){
l = null
break;
}
}
if (l) {
console.log('你點擊了ul里的li')
}
})
事件委托的優點:
- 減少監聽器
- 監聽動態內容
上面寫的事件委托的代碼可以解決大部分事件,但是不一定百分之百沒問題,還是要具體事件具體分析。