面试题:js事件流

我们经常会碰到这么一个现象,我们在逛社区的时候,看到优秀的文章,我们就会给他点赞,就像这样:

我们可以不需要点进去文章就可以直接点赞,按照正常的逻辑,我们应该点进去才能点赞的,因为点赞应该是内层的元素,我们怎么能隔山打牛穿过它的父级元素执行点赞这个事件呢。

这就与我们接下来要学习的事件流 息息相关了,在 JavaScript 中,事件触发过程涉及两个主要机制:事件捕获 (Event Capturing)和事件冒泡(Event Bubbling),这两个机制的默认顺序是事件冒泡,但我们也可以通过调用事件对象的方法来切换到事件捕获模式。

示例代码:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    *{
        margin: 0;
        padding: 0;
    }
    #a{
        width: 400px;
        height: 400px;
        background-color: #b92929;
    }
    #b{
        width: 200px;
        height: 200px;
        background-color: #1caa55;
    }
    #c{
        width: 100px;
        height: 100px;
        background-color: #000000;
    }
</style>
<body>
    <div id="a">
        <div id="b">
            <div id="c">
            </div>
        </div>
    </div>
</body>
<script>
    let a = document.getElementById("a");
    let b = document.getElementById("b");
    let c = document.getElementById("c");
    a.addEventListener("click",function(){
        console.log("点击a");
    },true)
    b.addEventListener("click",function(){
        console.log("点击b");
    },true)
    c.addEventListener("click",function(){
        console.log("点击c");
    },true)
</script>
</html>

事件捕获机制

概念:在事件触发的过程中,浏览器首先从文档根元素开始向里面寻找目标元素,这个过程称为事件捕获。通过事件捕获,可以在事件到达目标元素之前捕获到事件,并执行相应的处理函数。

在上面代码中,我们将监听事件的第三个参数设置为true,这样我们就可以切换到事件捕获模式,当我们点击c时,浏览器从根元素开始向里面开始寻找目标元素,然后在这个过程中依次捕获到了点击事件a,点击事件b,最后才找到目标元素c,捕获到点击事件c,所以会依次打印 点击a 点击b 点击c

事件冒泡机制

概念:一旦事件到达目标元素并执行相应的处理函数后,它会开始在目标元素上方的父元素中冒泡,直到到达文档根元素。这个过程称为事件冒泡。通过事件冒泡,可以在事件冒泡阶段捕获到事件,并执行相应的处理函数。

我们不做任何设置时(没有设置监听事件的第三个参数),默认就是冒泡机制,也很好理解,其实就是和捕获机制相反的一个过程,就好比你往墙上丢了一个球,这个球会反弹回来,这个球往墙上飞的过程就是事件的捕获过程,而球弹回来的过程就是冒泡的过程。我们点击c,浏览器找到目标元素时,就会向外层的父级元素冒泡,所以在这个示例中,我们依次会执行点击事件c、点击事件b、点击事件a,打印结果:点击c 点击b 点击a

stopPropagation 和 stopImmediatePropagation

那么如果我们不希望事件继续传播,还是这个例子,假设在默认情况下,我们点击了c,然后只想打印c而不希望触发点击事件b和点击事件a,那我们该怎么做呢

在Javascript有两个常用的方法:

stopPropagation()stopImmediatePropagation() 都是用于阻止事件进一步传播的方法,但它们在功能和作用上有一些不同。

  1. stopPropagation()stopPropagation() 方法用于阻止事件的冒泡或捕获过程。当我们调用 stopPropagation() 方法时,事件将停止在当前元素上触发其他相同类型的事件处理函数,并停止继续向上或向下传播到其他元素。这意味着其他父级或子级元素上的此类的事件处理函数将不会被触发。但如果当前元素上绑定了多个相同类型的事件处理函数,除了当前正在执行的处理函数外,其他处理函数仍然会被执行。 如下: 只会打印 点击c和点击d
js 复制代码
    a.addEventListener("click",function(){
        console.log("点击a"); // 阻止事件冒泡,其他父级元素上的点击事件处理函数不会被触发
    })
    b.addEventListener("click",function(){
        console.log("点击b");
    })
    c.addEventListener("click",function(e){
        e.stopPropagation()
        console.log("点击c");
    })
    c.addEventListener("click",function(e){
        console.log("点击d");
    })
  1. stopImmediatePropagation()stopImmediatePropagation() 方法也用于阻止事件的传播,但它的作用更为强大。当您调用 stopImmediatePropagation() 方法时,事件将立即停止,并且不会再触发当前元素上剩余的任何相同类型的事件处理函数,也不会继续传播到其他元素。这包括当前元素上绑定的其他事件处理函数,以及同级别元素上的事件处理函数。换句话说,除了当前正在执行的处理函数外,其他任何处理函数都不会被执行。

如下只会打印点击c

javascript 复制代码
    a.addEventListener("click",function(){
        console.log("点击a");
    })
    b.addEventListener("click",function(){
        console.log("点击b");
    })
    c.addEventListener("click",function(e){
        e.stopImmediatePropagation()  // 阻止事件传播并停止执行后续的处理函数
        console.log("点击c");
    })
    c.addEventListener("click",function(e){
        console.log("点击d");
    })

这就可以解释为什么这里可以"隔山打牛"了

相关推荐
Martin -Tang29 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发30 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂2 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽5 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新6 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html