likes
comments
collection
share

Javascript中的事件冒泡与事件捕获

作者站长头像
站长
· 阅读数 30

定义

事件冒泡和捕获分别由微软公司和网景公司提出,都是用来解决页面中事件流的问题,也就是解决事件发生顺序的问题。 举个例子:

<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)

如果您觉得我的文章有用,欢迎点赞和关注,也欢迎光临我的个人博客 github.com/BokFang

转载自:https://juejin.cn/post/6844904021849800712
评论
请登录