在了解什么是DOM事件以及給DOM事件綁定監聽器的幾種方法后,我們來談談事件委托。
1. e.target 和 e.currentTarget
當我們給目標元素target 綁定一個事件監聽器target.addEventListener(event,function(){}), 並指定回調函數function(e), 函數的參數e表示事件。此時e.target 與 e.currentTarget分別表示自己觸發事件的元素與被監聽的元素
<html>
<body>
<ul style='list-style:none;max-width:200px;border:1px solid;'>
<li>點我試試</li>
<ul>
<script>
(document.querySelector('ul')).addEventListener('click' ,function(e){
console.log('e.target')
console.log(e.target)
console.log('e.currentTarget')
console.log(e.currentTarget)
})
</script>
</body>
</html>
x
1
<html>
2
<body>
3
<ul style='list-style:none;max-width:200px;border:1px solid;'>
4
<li>點我試試</li>
5
<ul>
6
<script>
7
(document.querySelector('ul')).addEventListener('click' ,function(e){
8
console.log('e.target')
9
console.log(e.target)
10
console.log('e.currentTarget')
11
console.log(e.currentTarget)
12
})
13
</script>
14
</body>
15
</html>
16
這段代碼為一個ul元素綁定了一個監聽器,當ul上發生點擊事件時,分別輸出e.target和e.currentTarget的值。
當點擊ul中的li元素時,該段代碼的在控制台輸出的結果如下:
此時,e.target為直接點擊的元素li,而e.currentTarget為被監聽的元素ul。由此我們可以得到一個啟發,觸發事件的元素與被監聽的元素不一定是一個元素。於是就來到了本文的重點內容——事件委托。
2. 如何進行事件委托
什么情況下回用到事件委托呢?舉兩個例子
1) 當存在多個元素可以共用同一個監聽器
<body>
<ul>
<li>點<span>這里</span></li>
<li>點這里</li>
</ul>
<style>
ul, li{
list-style:none;
border:1px solid;
padding:10px;
background:#ddd
}
li{
text-align:center;
margin:10px;
background:#fff
}
</style>
</body>
</html>
x
1
<body>
2
<ul>
3
<li>點<span>這里</span></li>
4
<li>點這里</li>
5
</ul>
6
<style>
7
ul, li{
8
list-style:none;
9
border:1px solid;
10
padding:10px;
11
background:#ddd
12
}
13
li{
14
text-align:center;
15
margin:10px;
16
background:#fff
17
}
18
</style>
19
</body>
20
</html>
21
上面的代碼中定義了如圖所示的一個ul,它包裹着兩個li,第一個li中還有一個子元素span。如果我們希望點擊兩個li均執行同一條命令時,第一種方法是為每個li都綁定一個監聽器,但當li很多時,這樣處理就過於繁瑣。
這時我們會想到可以直接監聽ul,為ul綁定事件函數,那么只要li存在於ul的內部,點擊任意的一個li都會執行這條命令。但同樣存在一個問題,當點擊li的外部,也就是圖中的灰色區域時,命令同樣會被執行。
那么如何僅僅在點擊li的覆蓋區域的時候才執行這段命令呢,可以用以下這段代碼。
var ul=document.querySelector('ul')
ul.addEventListener('click',function(e){
var el = e.target
//判斷當前點擊的元素是否為li,如果不是,執行以下的while循環
while(el.tagName !== 'LI'){
//如果點擊的元素為ul,直接跳出循環
if(el === ul){
el = null
break;
}
//否則,將當前元素父元素賦給el
el=el.parentNode
}
//如果最后el不為null,則打出'ok'
if(el){
console.log('ok')
}
//否則,打出'你點擊的不是li'
else console.log('你點擊的不是li')
})
x
1
var ul=document.querySelector('ul')
2
ul.addEventListener('click',function(e){
3
var el = e.target
4
//判斷當前點擊的元素是否為li,如果不是,執行以下的while循環
5
while(el.tagName !== 'LI'){
6
//如果點擊的元素為ul,直接跳出循環
7
if(el === ul){
8
el = null
9
break;
10
}
11
//否則,將當前元素父元素賦給el
12
el=el.parentNode
13
}
14
//如果最后el不為null,則打出'ok'
15
if(el){
16
console.log('ok')
17
}
18
//否則,打出'你點擊的不是li'
19
else console.log('你點擊的不是li')
20
})
21
這段代碼實現了當點擊的區域在li范圍內時,不管點擊的是li元素本身,還是li的子元素span,都會執行console.log('ok'),但當點擊區域超出li的范圍,則執行' console.log('你點擊的不是li')'。這就成功實現了一個事件委托。
(2) 用事件委托實現動態監控
還有一種情況,也會用到事件委托,那就是需要動態監控的時候。
看以下代碼:
<html>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<button id=addButton>+</button>
</body>
</html>
<style>
li{
border: 1px solid;
}
</style>
<script>
addButton.onclick = function(){
var li = document.createElement('li')
li.textContent = 'new'
document.querySelector('ul').appendChild(li)
}
document.querySelector('ul').onclick = function(e){
console.log(e.target)
}
</script>
</body>
</html>
x
1
<html>
2
<body>
3
<ul>
4
<li>1</li>
5
<li>2</li>
6
<li>3</li>
7
<li>4</li>
8
</ul>
9
<button id=addButton>+</button>
10
</body>
11
</html>
12
<style>
13
li{
14
border: 1px solid;
15
}
16
</style>
17
<script>
18
addButton.onclick = function(){
19
var li = document.createElement('li')
20
li.textContent = 'new'
21
document.querySelector('ul').appendChild(li)
22
}
23
document.querySelector('ul').onclick = function(e){
24
console.log(e.target)
25
}
26
</script>
27
</body>
28
</html>
29
這段代碼實現的效果如下:
當點擊左下角的加號按鈕時,會增加一個新的li,同時在點擊li時,在控制台輸出被點擊的li的內容。這就是用事件委托實現動態監控。
