基本概念
把想要被监听的元素委托给它的父元素或祖先元素来监听。
实例分析
假如有个button标签,当用户每次点击时,会在有序列表‘ol’里添加一个列表‘li’,给每个‘li’绑定上监听事件。当用户点击某一‘li’时,会执行某种操作,这里我们假定绑定的监听事件是把’li‘标签删除。
HTML代码如下:1
2<button id="takenumber">+</button>
<ol id="numbers"></ol>
JS代码如下:
1 | takenumber.addEventListener('click',function(e){ |

上面代码是对每个动态生成的’li‘都进行了监听,然而这种监听会存在一个缺点,假如我生成一万个’li‘标签,岂不是要有1万个监听函数。这极大地浪费了内存。
此时,如果我们用事件委托的话就不会存在这种问题。
事件委托
- 监听还不存在的元素
- 减少监听器个数(用代码来换内存)
JavaScript代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14takenumber.addEventListener('click',function(e){
let number = Math.round(Math.random()*100)
let li = document.createElement('li')
li.textContent = number
numbers.appendChild(li)
}
//监听父元素
numbers.addEventListener('click',function(e){
//e.target表示用户点击的元素
let elment = e.target
if(element.tagName === 'LI'){
element.remove()
}
}

通过以上的代码即可实现事件委托,然而这是一个有bug的事件委托。假如生成的’li‘标签里还有子元素’span‘,当用户点击的元素不是’li‘,而是’span‘时,根本就不会删除这个’li‘标签。如此,我们需要改善上面的JS代码。
修复事件委托的bug
当用户点击某个元素时,我们判断这个元素的父元素的’tagName‘是否为’LI‘,可以用if…else判断,此时我们还需考虑的一个问题是,假如’li‘标签下嵌套了多层,那就不仅仅是判断父元素了,还得判断其父父元素甚至更多,于是我们用while循环来判断。JS代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18takenumber.addEventListener('click',function(e){
let number = Math.round(Math.random()*100)
let li = document.createElement('li')
li.textContent = number
numbers.appendChild(li)
}
numbers.addEventListener('click',function(e){
let element = e.target
while(element.tagName !== 'LI'){
if(element === numbers){
element = null //无操作
break
}
element = element.parentNode
}
//有假返假,都真返lastone
element && element.remove()
}
混淆点
事件机制不是有三大共识吗?其中有一条为:
child被点击了,意味着parent也被点击了。
点击是不存在冒泡的,并不是说你点了child,就相当于点了parent,它只是一个通知阶段,通过冒泡的形式通知parent,你的子元素child被点击了。e.target始终是你点击的元素,并不存在什么冒泡,冒泡只是一个通知机制。