定义
事件冒泡和捕获分别由微软公司和网景公司提出,都是用来解决页面中事件流的问题,也就是解决事件发生顺序的问题。
举个例子:
<div id="a1">
<p id="a2"></p>
</div>
如果我在a2上挂载一个click的处理函数,并在页面点击a2,那么是a1还是a2上注册的事件先被触发呢?
事件冒泡
微软公司的IE提出了事件冒泡的事件流。事件冒泡是什么意思呢?我们可以将其比喻为一个在水底的气泡,这个气泡在水里会不断往上升,直到浮出水面。事件冒泡就是类似这样一个从底往上的过程,事件会从最内层的元素开始发生,发生顺序一直向上,直到document。
也就是说,假设我们在前面的例子点击a2,那么事件发生的顺序为:
p -> div -> body -> html -> document
事件捕获
而网景公司提出的是事件捕获的事件流。如果说事件冒泡像从水底升上去的一个气泡,那么事件捕获则像是一块扔进水底的大石头。与事件冒泡相反,事件捕获的顺序是从最外层到最里层的元素:
document -> html -> body -> div -> p
addEventListener
mdn上的说明:
EventTarget.addEventListener(event, function, useCapture)方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,指定的回调函数就会被执行。
这个方法输入三个参数。
第一个参数是要挂载的事件,比如click;
第二个参数输入的是发生事件对应的函数,如将div变色;
第三个参数输入的是一个布尔类型,当输入为false时,则处理事件按照事件冒泡顺序;若输入为true,则处理事件按照事件捕获顺序。第三个参数的默认值为false。
false ——> 事件冒泡(默认)
true ——> 事件捕获
事件冒泡例子
<div id="a1">a1
<p id="a2">a2</p>
</div>
<script>
a1.addEventListener("click",function(e){
console.log('a1 冒泡')
},false)
a2.addEventListener("click",function(e){
console.log('a2 冒泡')
},false)
</script>
结果:
点击a1时,控制台出现a1 冒泡
;点击a2时,控制台出现a2 冒泡 a1冒泡
事件捕获例子
<div id="a1">a1
<p id="a2">a2</p>
</div>
<script>
a1.addEventListener("click",function(e){
console.log('a1 捕获')
},true)
a2.addEventListener("click",function(e){
console.log('a2 捕获')
},true)
</script>
结果:
点击a1时,控制台出现a1 捕获
;点击a2时,控制台出现a1 捕获 a2捕获
同时有事件冒泡和事件捕获
我们先看看例子和执行结果:
<div id="a1">a1
<div id="a2">a2</div>
</div>
<script>
a1.addEventListener("click",function(e){
console.log("a1 冒泡");
},false);
a2.addEventListener("click",function(e){
console.log("a2 冒泡");
},false);
a1.addEventListener("click",function(e){
console.log("a1 捕获");
},true);
a2.addEventListener("click",function(e){
console.log("a2 捕获");
},true);
</script>
结果:
点击a1,控制台出现a1 冒泡
a2 捕获
点击a2,控制台先后出现a1 捕获
a2 冒泡
a2 捕获
a1 冒泡
根据结果我们可以得出过程:
1.从document开始往被点击的节点捕获前进,遇到注册的捕获事件就立刻执行该事件
2.到达被点击的节点后执行注册的事件
3.执行完被点击的节点上的事件后,冒泡前进,遇到注册的冒泡事件就立刻执行该事件
总结
1.对于非点击的节点来讲,先执行捕获再执行冒泡
2.对于被点击的节点来讲,则是按照顺序执行先注册的事件
实际应用:事件代理
利用这个事件流的特性,我们可以使用事件代理这种方法。
<ul id="color">
<li>red</li>
<li>orange</li>
<li>yellow</li>
<li>green</li>
<li>blue</li>
<li>purple</li>
</ul>
假设要我们点击页面中的li元素,然后输出对应的颜色,我们可能会用循环来解决,但使用循环的缺点是每次循环都会创建一个新的函数来绑定事件,这样的性能损耗比较大。而如果使用事件代理方法,利用事件流的特征,我们只绑定一个事件处理函数也可以完成:
color.addEventListener("click",(e)=>{
let a = e.target
if(a.nodeName === li){
console.log("The color is" + a.innerHTML)
}
},false)