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')
}
})
事件委托的优点:
- 减少监听器
- 监听动态内容
上面写的事件委托的代码可以解决大部分事件,但是不一定百分之百没问题,还是要具体事件具体分析。