事件监听器的第三个参数?一篇文章带你透彻理解JS的事件流

前言

在原生JS开发的那个"遥远年代",当我们试图监听用户的操作从而与用户进行交互的时候,经常需要我们去获取页面的dom元素并添加事件监听器。但是随着互联网的高速发展,单纯地通过获取dom元素添加事件监听器已经无法满足用户需求了,这个时候就需要去深入理解JS事件流这一特性,以帮助我们更好地完成开发。

正文

捕获与冒泡

若要各位读者老爷讲明白捕获和冒泡这两个概念,就不得不掏出我用了多年的一个小小demo了

html 复制代码
<div id="app">
    <div id="wrap">
        <div id="box"></div>
    </div>
</div>
css 复制代码
        #app {

            width: 400px;

            height: 400px;

            background-color: #ca6363;

        }

        #wrap {

            width: 200px;

            height: 200px;

            background-color: blueviolet;

        }

        #box {

            height: 100px;

            width: 100px;

            background-color: chartreuse;

        }

在这个demo中,很明显看到三个div,且由下到上逐渐减小,现在我给这三个容器添加点击事件

js 复制代码
const app = document.getElementById('app')
const wrap = document.getElementById('wrap')
const box = document.getElementById('box')

app.addEventListener('click', () => { console.log('app'); })
wrap.addEventListener('click', () => { console.log('wrap'); })
box .addEventListener('click', () => { console.log(box ); })

那么,现在当我点击一下最顶上的box会发生什么?

很明显,在浏览器看来,我同时点到了他们仨,这合情合理,毕竟你都点在我肾上了你总不能说没点我身上吧?那么,问题来了,为什么会是这么个打印顺序?你可能会说:这不是理所应当的吗?点击肯定是先点到最上面的,所以就先触发最上面的事件监听器打印box咯。但事实相反,当你点击的那一瞬间,你的点击事件是从最底层的window开始,传到html上,再到app,wrap,最后到顶层的box上面,而这一从底到顶的传播,就叫捕获。那既然是从底到顶,为什么事件的触发不是从底到顶呢。因为在window上往事件触发处传播,遇到注册的捕获事件会触发,简单点说,就是在捕获的过程中遇到的事件都是不可以在捕获阶段被触发的。

捕获结束后,也就到了最顶端容器,这个时候在一层层往window回去,直到回到window的过程就叫冒泡。在这个过程中遇到的事件就会触发掉。所以这也是为什么我们看到的打印顺序是app -> wrap -> box了。

事件监听器的第三个参数

在我刚刚的小demo中给出的事件监听器以及我们常用的事件监听器都是只有两个参数,但实际上,addEventListener还可以接受第三个参数true或false。那这个参数是用来干什么的?在刚刚聊到的捕获和冒泡中,我提到之所以在捕获的过程中不会触发函数是因为遇到的事件都是不可以在捕获阶段被触发的,而造成这一点的原因就是addEventListener的第三个参数默认为false,那如果我们都给设置为true,意味着使其变成能够在捕获阶段被触发,打印结果是否会按照捕获的顺序来呢?还是那句话,实践出真知

js 复制代码
app.addEventListener('click', () => { console.log('app'); }, true)
wrap.addEventListener('click', () => { console.log('wrap'); },true)
box .addEventListener('click', () => { console.log(box ); },true)

结果如下

JS事件流的实际应用

但是实际开发中经常会有多个容器的嵌套,就拿掘金官网来说

当我们点击文章标题或者其他文字部分的时候,我们是会直接打开这篇文章的,但是如果我们点击的是大拇指的按钮,我们只会给这篇文章点一个赞,并不会打开这篇文章。

而很明显的是,这个大拇哥很明显是属于这篇文章的容器内的,那为什么我们点击大拇哥不会打开文章呢?这就得靠stopPropagation了。stopPropagation是时间监听器自带的一个方法,其作用就是阻止默认事件的传播,例如当我们在box元素上调用stopPropagation函数时,事件流到达box之后就会被阻断,不会继续冒泡到wrap和app身上

js 复制代码
app.addEventListener('click', () => { console.log('app'); })
wrap.addEventListener('click', () => { console.log('wrap'); })
box .addEventListener('click', () => {
console.log('box');
stopPropagation()
})

既然都提到了stopPropagation,那就不得不多嘴提一下stopImmediatePropagation了。相信不用我说,光靠眼睛都能发现它比上面那个玩意多一个单词。而正所谓一寸长一寸强,stopPropagation能干的,它能干,stopPropagation不能干的,它也能干。那么具体点说,就是stopImmediatePropagation能够阻止同一dom元素上绑定多个相同的事件,完整的代码如下

js 复制代码
const app = document.getElementById('app')
        const wrap = document.getElementById('wrap')
        const box = document.getElementById('box')


        app.addEventListener('click', () => { console.log('app'); },true)//绑定,订阅,注册
        wrap.addEventListener('click', () => { console.log('wrap'); },true)
        //事件监听的第三个参数默认就是false,表示这个事件会在冒泡过程被触发,而true则是在捕获过程被触发
        box.addEventListener('click', (e) => {
            // 点击box之后只打印box而不打印其他,需要阻止冒泡的继续发生
            console.log('box');
            e.stopImmediatePropagation()
            // stopImmediatePropagation会阻止事件流的传递同时阻止同一个dom元素的相同事件发生
          
        },true)
        box.addEventListener('mouseenter', (e) => {
            // 这里监听的是mouseenter事件而不是click,所以不会被阻止
            console.log('mouseenter');

        })
        box.addEventListener('click', (e) => {
            // 这里监听的还是box的click事件,会被阻止
            console.log('box2');

        })

总结

JavaScript事件流描述了事件在DOM元素中传播和触发的顺序。它分为捕获阶段、目标阶段和冒泡阶段。在捕获阶段,事件从根节点向目标元素传播,然后在目标阶段触发,最后在冒泡阶段从目标元素向根节点传播。通过理解事件流,我们可以更好地控制事件处理程序的执行顺序,以及在需要时阻止事件的传播或默认行为的发生。

相关推荐
Avan_菜菜6 小时前
AI 能写代码了,为什么我反而开始要求它先写文档?
前端·github·ai编程
JieE2129 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2129 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
爱勇宝10 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒13 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen13 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher14 小时前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙14 小时前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
牧艺14 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
jump_jump14 小时前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化