前端实现捕获与冒泡

捕获与冒泡是浏览器DOM事件处理的重要概念。本文介绍了它们的工作原理,通过示例解释了事件从目标元素到父元素的传递过程。并探讨了如何使用addEventListener的参数来阻止事件的捕获和冒泡,以及解决只触发特定元素事件的问题。建议阅读《javascript高级程序设计》以深入了解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端实现捕获与冒泡

什么是捕获与冒泡

捕获与冒泡是浏览器dom流事件的概念,通俗讲就是鼠标事件如何触发的,流程是什么,比如点击某个dom元素,我们肯定能监听本次点击事件,一般来讲这次事件可以描述为,“我监听了某次点击事件,并且设置了当点击时应该发生什么,当我点击时,应该触发我预先设定好的任务”。当然,这是理所应当我们想要的情况。但是有时候情况并不像我们想的那样,比如看下面这段代码:

<style>
    .box {
        width: 200px;
        height: 200px;
        background: gainsboro;
        display: flex;
        align-items: center;
        justify-content: center;
    }
</style>
<div class="box" id="box">
        <button id="btn">click me</button>
</div>
<script>
        document.getElementById("box").onclick = function () {
            console.log("点击了 box 元素, 应该做点什么...");
        }
        document.getElementById("btn").onclick = function () {
            console.log("点击了 button 元素, 应该做点什么...");
        }
</script>

很显然,这段代码意思是,给 div#box 添加点击事件,给 button#btn 也添加点击事件,当我们点击 button 时控制台会打印如下:
在这里插入图片描述
也就是说,我们点击 button 元素时同时相当于点击了 div#box,这是为什么呢?也许你会想,这个简单,div 元素里包裹着 button 元素,当我们点击 button 时其实相当于点击了 div 元素。这样想没有错,但是问题是如果我们只想当点击 button 时只触发 button 的事件,而不触发 div的事件,我们该怎么办呢?或者说当我们点击 button 时只触发 div 的事件,而不触发 button 的事件应该怎么做呢?首先我们需要明白事件是怎么发生的,请看下面这张图(盗的):
在这里插入图片描述
让我们来描述下上图过程,假设为点击事件: 首先鼠标点击 text 元素, 这时候浏览器需要定位到点击了哪个目标元素,它不会直接定位到那个 text 元素,而是一步步传递事件,左侧为捕获过程,也就是查找 text 元素时触发的事件,首先时 window (即窗口),然后时 document 文档,之后是body元素,然后是 div元素,然后是 text 元素,这个过程可以想象成一棵树的深度遍历过程,右侧为冒泡事件,即事件传递到目标元素以后并不会终止,而是继续向上传递,即从叶子节点到根节点。以下代码可以验证上面所述:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
    <div id="box">
        <p id="text">click me!</p>
    </div>

    <script>
        /**Dom.addEventListener 方法为监听 dom 元素的事件
         * addEventListener(type, listener, useCapture)
         * @ type 监听事件类型的字符串
         * @ listener 一个函数或者一个实现EventListener接口的对象
         * @ useCaptrue 是否使用捕获 true -> 捕获,false -> 冒泡,不填为冒泡  
        */
        
        //监听 window 点击捕获事件
        window.addEventListener("click", function (event) {
            console.log("点击了window,应该做点什么...", event.eventPhase);
        }, true);
        //监听 window 点击冒泡事件
        window.addEventListener("click", function (event) {
            console.log("点击了window,应该做点什么...", event.eventPhase);
        }, false);


        //监听 document 点击捕获事件
        document.addEventListener("click", function (event) {
            console.log("点击 document,应该做点什么...", event.eventPhase);
        }, true);
        //监听 document 点击冒泡事件
        document.addEventListener("click", function (event) {
            console.log("点击 document,应该做点什么...", event.eventPhase);
        }, false);


        //监听 body 点击捕获事件
        document.body.addEventListener("click", function (event) {
            console.log("点击了 body,应该做点什么...", event.eventPhase);
        }, true);
        //监听 body 点击冒泡事件
        document.body.addEventListener("click", function (event) {
            console.log("点击了 body,应该做点什么...", event.eventPhase);
        }, false);


        //监听 div 点击捕获事件
        document.getElementById("box").addEventListener("click", function (event) {
            console.log("点击了div, 应该做点什么...", event.eventPhase);
        }, true);
        //监听 div 点击冒泡事件
        document.getElementById("box").addEventListener("click", function (event) {
            console.log("点击了div, 应该做点什么...", event.eventPhase);
        }, false);


        //监听 p 点击捕获事件
        document.getElementById("text").addEventListener("click", function (event) {
            console.log("点击了p元素,应该做点什么...", event.eventPhase);
        }, true);
        //监听 p 点击冒泡事件
        document.getElementById("text").addEventListener("click", function (event) {
            console.log("点击了p元素,应该做点什么...", event.eventPhase);
        }, false);
    </script>
</body>
</html>

下面是点击 p 元素的浏览器打印台结果:
在这里插入图片描述
结合图一目了然,你可能会疑问后面的数字什么意思,在这里说明一下 event.eventPhase 这个值表示当前事件是捕获还是冒泡还是目标元素触发,1 -> 捕获,2 -> 点击目标元素,3 -> 冒泡。也就是说对于目标元素的捕获和冒泡是相同的。还记得上面的问题吗,就是点击 button 元素 只触发 button的事件,或者只触发 div的事件,也就是说我们如何阻止 捕获,冒泡事件的传递。看下面这段代码:

	<div id="box">
        <button id="btn">click me</button>
    </div>

    <script>
        document.getElementById("box").addEventListener("click", function (event) {
            console.log("点击了box 元素,应该做点什么...");
            event.stopImmediatePropagation();
        }, true);

        document.getElementById("btn").addEventListener("click", function (event) {
            console.log("点击了 button 元素,应该做点什么...");
        }, true);
        document.getElementById("btn").addEventListener("click", function (event) {
            console.log("点击了 button 元素,应该做点什么...");
        }, false);
    </script>

在这里我们为了更好的验证,你可以看到,我们为 button 点击监听了捕获以及冒泡,当我们点击button元素时,你会看到:
在这里插入图片描述
当然,你没有看到 “点击了 button 元素,应该做点什么…” 字样,如果你仔细看了代码,肯定会知道是这行代码的作用:

	event.stopImmediatePropagation();

没错,就是这行代码的作用,注意这个方法有一个差不多的方法:

	event.stopPropagation();

显然这两个方法差 Immediate ,对于前者 会停止当前绑定的事件以及后续所有的事件,但是对于后者 会先执行完当前的事件,然后停止后面的事件。
到此你已经看到几个关键的设置都是通过 函数参数 event 的设置实现的,所以你可以查阅相关资料了解更多关于 这个函数参数的作用,推荐阅读 《javascript高级程序设计》《javascript权威指南》,你也可以在网上查阅更多相关 addEventListener 这个函数的资料。上面说到,是这行代码阻止了捕获,其实这行代码的意思是 阻止捕获以及冒泡的继续传递 上面验证了其阻止捕获,你可以自行编码验证组织冒泡。对于我们刚开始提到的问题,可用一下代码解决:

<!-- 只触发div的事件 ->
<div id="box">
    <button id="btn">click me</button>
</div>

<script>
    document.getElementById("box").addEventListener("click", function (event) {
        console.log("点击了box 元素,应该做点什么...");
        event.stopImmediatePropagation();
    }, true);

    document.getElementById("btn").addEventListener("click", function (event) {
        console.log("点击了 button 元素,应该做点什么...");
    }, true);
    document.getElementById("btn").addEventListener("click", function (event) {
        console.log("点击了 button 元素,应该做点什么...");
    }, false);
</script> 

<!-- 只触发button的事件 ->
<div id="box">
    <button id="btn">click me</button>
</div>

<script>
    document.getElementById("box").addEventListener("click", function (event) {
        console.log("点击了box 元素,应该做点什么...");
        event.stopImmediatePropagation();
    }, false);

    document.getElementById("btn").addEventListener("click", function (event) {
        console.log("点击了 button 元素,应该做点什么...");
        event.stopImmediatePropagation();
    }, true);
</script>

在这里说明一下,如果你把 div 的事件绑定在捕获阶段,那你是无论如何都不可能只触发 button 的事件的,所以把 dom 元素的事件绑定在捕获还是冒泡阶段是很关键的。总之,本文只是简单介绍了一下捕获与冒泡,更多关于捕获与冒泡这些 dom 流事件的信息,请自行查阅,强烈推荐《javascript高级程序设计》这本书。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值