likes
comments
collection
share

面试官:js中的事件触发过程是什么样子的?

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

今天我们来聊一下一个简单的面试题!描述一下js中的事件触发过程!

js中的事件捕获和事件冒泡

在JavaScript中,事件传播机制包括两种主要方式:事件捕获(Capturing)和事件冒泡(Bubbling)。这两种方式决定了当在DOM(文档对象模型)中的某个元素上触发一个事件时,该事件如何在该元素及其祖先元素之间传播。

事件捕获(Event Capturing)

事件捕获阶段从DOM树的最顶层开始,即window对象,然后逐级向下,直到触发事件的目标元素。在这个过程中,事件会从最不具体的节点(通常是documentwindow)开始,沿着DOM树向下传播到最具体的节点(即事件的目标元素)。如果在捕获阶段有事件监听器被触发,并且它们中的任何一个调用了event.stopPropagation()方法,那么事件传播会立即停止,不会到达目标元素。

在事件监听器中,你可以通过将第三个参数设置为true来指定在捕获阶段处理事件:

element.addEventListener('click', function(event) {  
  // 处理事件  
}, true); // 第三个参数为true,表示在捕获阶段处理事件

事件冒泡(Event Bubbling)

事件冒泡阶段则正好相反,它从触发事件的目标元素开始,然后逐级向上传播,直到最顶层的window对象。在冒泡阶段,事件会从最具体的节点开始,然后沿着DOM树向上传播到最不具体的节点。如果在冒泡阶段有事件监听器被触发,并且它们中的任何一个调用了event.stopPropagation()方法,那么事件传播会立即停止,不会继续向上传播。

默认情况下,如果你没有指定事件监听器应该在哪个阶段处理事件,那么它会在冒泡阶段处理事件。你也可以通过将第三个参数设置为false来明确指定在冒泡阶段处理事件:

element.addEventListener('click', function(event) {  
  // 处理事件  
}, false); // 第三个参数为false或省略,表示在冒泡阶段处理事件

事件传播顺序

事件传播的完整顺序是:捕获阶段 -> 目标元素 -> 冒泡阶段。也就是说,如果一个元素及其祖先元素都注册了相同类型的事件监听器,那么这些监听器将按照上述顺序被触发。

阻止事件传播

event.stopPropagation()方法用于阻止事件在DOM树中进一步传播。无论是在捕获阶段还是冒泡阶段,调用这个方法都会阻止事件继续传播。

event.stopImmediatePropagation()方法的行为与event.stopPropagation()类似,但它还会阻止同一元素上的多个相同事件被触发。

js事件触发的过程

我们来通过一个小Demo来具体阐述一下js事件的触发过程

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app{
            width: 400px;
            height: 400px;
            background-color: aqua;
        }
        #wrap{
            width: 200px;
            height: 200px;background-color: rebeccapurple;
        }
        #box{
            width: 100px;
            height: 100px;
            background-color: darkorange;
        }
    </style>
</head>
<body>
    <div id="app">
        <div id="wrap">
            <div id="box">

            </div>
        </div>
    </div>
    <script>
        let app = document.getElementById("app");
        let wrap = document.getElementById("wrap");
        let box = document.getElementById("box");

        // addEventListener有第三个参数,默认是false,true表示在捕获阶段触发,false表示在冒泡阶段触发
        app.addEventListener('click',()=>{//绑定,订阅,注册
            console.log("app")
        })

        wrap.addEventListener('click',()=>{
            console.log("wrap")
        })

        box.addEventListener('click',()=>{
            console.log("box")
        })
    </script>
</body>

我们写了三个嵌套容器app->wrap->box

我们可以用一个图来描述js的事件触发过程!

面试官:js中的事件触发过程是什么样子的?

事件触发过程分为三个阶段:事件捕获、目标元素阶段和事件冒泡。

  1. 事件捕获:这是事件传播的第一个阶段,它从DOM树的最顶层(通常是window对象或document对象)开始,沿着DOM树向下传播,直到触发事件的具体元素。在这个过程中,如果某个节点注册了捕获阶段的事件监听器,那么该监听器会在事件到达目标元素之前被触发。
  2. 目标元素阶段:当事件到达触发它的具体元素时,该元素上注册的事件监听器会被触发。这是事件传播的第二个阶段。
  3. 事件冒泡:这是事件传播的第三个阶段,它从目标元素开始,然后沿着DOM树向上传播,直到最顶层的window对象。在这个过程中,如果某个节点的祖先元素注册了冒泡阶段的事件监听器,那么这些监听器会被依次触发。

大致过程:

  1. 在window上往事件触发处传播,遇到注册的 捕获 事件会触发(我们可以让他触发)

  2. 到达了事件触发处

  3. 从事件触发处往window上传播,遇到的注册的 冒泡 事件会触发

从事件触发处,往window上传播,这个过程叫冒泡过程,这个过程中触发的所有事件就叫冒泡事件

从外层容器,一步一步向内层容器传播,这个过程叫捕获过程,这个过程触发的所有事件叫捕获事件

我们回到我们的Demo上来,现在我们的页面上有三个嵌套的容器,每个容器都添加了点击事件

面试官:js中的事件触发过程是什么样子的?

我们来点击一下box看看输出结果:

输出:
box
wrap
app

为什么是这样的结果呢?这个因为addEventListener有第三个参数,默认为false,所以事件的触发就默认在冒泡过程当中!也就是类似回溯的过程当中,从box容器,触发事件,传播到wrap容器,触发事件,再传播到app容器,触发事件。

如果我们把第三个参数修改为ture,我们再来看看输出结果

app.addEventListener('click',()=>{//绑定,订阅,注册
    console.log("app")
},true)

wrap.addEventListener('click',()=>{
    console.log("wrap")
},true)

box.addEventListener('click',()=>{
    console.log("box")
},true)

我们再点击一下box容器,此时的输出结果就会变为

输出:
app
wrap
box

当我们将第三个参数修改为ture,事件就会在捕获过程中被触发!也就是从最外层容器开始传播,从app容器,事件触发,传播到wrap容器,事件触发,传播到box容器,事件触发。

阻止默认事件传播的方法

当我们在box容器的事件监听器中加入这个方法event.stopPropagation();

app.addEventListener('click',()=>{//绑定,订阅,注册
    console.log("app")
},false)

wrap.addEventListener('click',()=>{
    console.log("wrap")
},false)

box.addEventListener('click',()=>{
    console.log("box")
    event.stopPropagation();
},false)

此时,我们再点击box容器,此时的输出结果只有box,说明这个方法会阻止冒泡过程的传播!

同样的,这个方法也能阻止捕获过程的传播!

app.addEventListener('click',()=>{//绑定,订阅,注册
    console.log("app")
    event.stopPropagation()
},true)

wrap.addEventListener('click',()=>{
    console.log("wrap")
},true)

box.addEventListener('click',()=>{
    console.log("box")

},true)

此时,我们在app容器中,加入这个方法,同时将事件监听器的第三个参数改为ture,在捕获过程中触发事件,我们再点击box容器看看输出结果!

此时输出只有app

也就是说event.stopPropagation();会阻止默认事件的传播。

同时,还有event.stopImmediatePropagation()

这个方法不仅仅包括stopPropagation()的功能,同时还可以阻止同一dom绑定多个相同事件

例如:

app.addEventListener('click',()=>{//绑定,订阅,注册
    console.log("app")

})

wrap.addEventListener('click',()=>{
    console.log("wrap")
})

box.addEventListener('click',()=>{
    console.log("box")
    event.stopImmediatePropagation()
})
box.addEventListener('click',()=>{
    console.log("box2")  
})
box.addEventListener('mouseenter',()=>{
    console.log("box3")
})

我们还是在冒泡过程处理事件,我们给box添加了两个点击事件和一个鼠标移入事件,一个输出box,一个输出box2,鼠标移入事件输出box3

我们再点击box容器,来看看输出结果!

面试官:js中的事件触发过程是什么样子的?

我们可以看到event.stopImmediatePropagation()不仅仅阻止了默认事件的传播,还会阻止同一dom结果绑定的多个相同的事件!

最后

今天我们主要学习了js中的事件触发过程,以及阻止事件传播的两个方法!

这个知识点对于我们在js中处理Dom事件,相当重要!可以帮助我们灵活地控制事件的处理方式,特别是在复杂的Dom结构和多个事件监听器的情况下。

如果,你觉得这篇文章有帮助的话,可以帮博主点赞+评论+收藏,三连一波!感谢!

往后,我还会持续输出相关的文章,如node相关的内置模块,koa的使用等等,感兴趣的小伙伴可以关注一波!