likes
comments
collection
share

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

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

前言

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

正文

捕获与冒泡

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

<div id="app">
    <div id="wrap">
        <div id="box"></div>
    </div>
</div>
        #app {

            width: 400px;

            height: 400px;

            background-color: #ca6363;

        }

        #wrap {

            width: 200px;

            height: 200px;

            background-color: blueviolet;

        }

        #box {

            height: 100px;

            width: 100px;

            background-color: chartreuse;

        }

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

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

 

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会发生什么?

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

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

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

 

事件监听器的第三个参数

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

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

结果如下

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

JS事件流的实际应用

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

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

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

而很明显的是,这个大拇哥很明显是属于这篇文章的容器内的,那为什么我们点击大拇哥不会打开文章呢?这就不得不提到阻止事件流的传递了。首先我们需要知道的是当我们在浏览器上点击的时候会生成一个事件对象,事无巨细地记录你点这下鼠标的所有参数(身为舔狗的我不得不对此表示大为感动,原来真有人对我这么认真)我们沿着原型链一路往上找,会发现一个神奇的小玩意那就是stopPropagation,它的作用就是阻塞事件流的传递,例如当我们给之前demo里面的box身上加上一个stopPropagation,那么当事件流从box开始冒泡的时候,执行完console.log('box');之后会被立刻截停,从而导致app和wrap身上的点击事件失效

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元素上绑定多个相同的事件,完整的代码如下

 

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

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