JavaScript的事件传播机制

news/2024/10/30 21:32:31/

你在学习和编写JavaScript时可能听说过事件冒泡(event bubbling)。它会发生在多个元素存在嵌套关系,并且这些元素都注册了同一事件(例如click)的监听器时。

但是事件冒泡只是事件机制的一部分。它经常与事件捕获(event capturing)和事件传播(event propagation)一起被提及。对于在JavaScript中处理事件来说,对这三个概念的透彻理解是非常必要的——例如,你希望实现事件委托模式。

什么是事件传播?

让我们从事件传播开始。这是事件冒泡和事件捕获的统称。以一个相册缩略图列表为例:
在这里插入图片描述
点击一张图片后不仅会为对应的 img 元素生成一个 click 事件,还会为它的父级 a 元素以及祖父级 li元素等上级元素生成一个 click 事件,一直到达该元素的最顶级上级元素,最后在 window 对象上终止。

用DOM术语来说,该图片是事件目标(event target),是最内层的元素,点击就是在这个元素上产生的。事件目标加上它的所有上级元素,从它的父级元素一直到window对象,在DOM树中形成了一个分支。例如,对于我们的例子来说,这个分支将由如下节点组成: img、a、li、ul、body、html、document、window。

注意window实际上并不是DOM节点,不过它实现了 EventTarget 接口,所以为了简便起见,我们为把它视为 document 对象的父级元素来处理。

这个分支很重要,因为它是事件传播的路径。这种传播是调用指定类型事件所有监听器的过程,这些监听器绑定在了这个分支上的节点上。每个监听器在调用时会有一个 event 对象,它含有关于当前事件的信息(稍后详细介绍)。

记得在一个节点上,可以为相同的事件类型注册多个监听器。当事件传播到达此节点时,监听器按照它们注册的顺序被调用。

事件传播是双向的,从window到事件目标,然后再返回。这种传播可以分为三个阶段:

  • 从window到事件目标的父级元素:这是捕获阶段
  • 事件目标自身:这是目标阶段
  • 从事件目标的父级元素回到 window:这是冒泡阶段

事件捕获阶段

在这一阶段,只有那些设置为捕获阶段工作的监听器才被调用。要为捕获阶段设置监听器,可以在调用 addEventListener 时设置第三个参数为 true:

在这里插入图片描述
如果省略此参数,则其默认值为false,该监听器在捕获阶段不工作,而是在冒泡阶段工作。

因此,在这个阶段,只有在从window到事件目标父级元素之间路径上找到的监听器才被调用。

事件目标阶段

在此阶段,将调用在事件目标上注册的所有监听器,而不管其捕获标志的值如何。

事件冒泡阶段

在事件冒泡阶段只有标记为非捕获的监听器才会被调用。也就是那些调用 addEventListener() 时第三个参数为 false 时注册的监听器。默认值即为false。

在这里插入图片描述
请注意,虽然所有事件都会在捕获阶段到达事件目标,不过有些事件,如focus、blur、load等,它们不会冒泡。也就是说,它们的事件传播在目标阶段后终止。

因此,在传播结束时,分支上的每个监听器都只被调用一次。

并非每种类型的事件都会冒泡。在传播过程中,监听器可以读取 event 对象的 .bubbles 属性来得知该事件是否冒泡。

W3C UIEvents规范的提供的如下图片演示了事件流的三个阶段。

在这里插入图片描述
访问事件传播信息

上面我提到了event对象的.bubbles属性。此对象提供了许多其他属性,可供监听器访问与传播相关的信息。

e.target 指向事件目标。
e.currentTarget 是正在执行的监听器注册到的节点。
我们可以用 e.eventPhase 得知当前的阶段。它的值是一个数字,1到3分别对应的是 Event 构造函数的常量 CAPTURING_PHASE, AT_TARGET 和 BUBBLING_PHASE。

整合在一起

我们把上面的概念付诸实践。我们的HTML代码如下:

在这里插入图片描述
event.js 内容如下:

在这里插入图片描述
我们为每个元素在捕获阶段和冒泡阶段都分别绑定了监听器,然后在浏览器中鼠标点击图片,在控制台中输出如下:

在这里插入图片描述
停止事件传播

事件传播是可以终止的,只需要在任意监听器中调用 event 对象的 stopPropagation() 方法。这意味着传播路径中当前节点之后节点上的所有监听器不会被调用了。不过,绑定在当前目标上的其他剩余监听器仍将调用。

我们仍使用上面的例子,现在想在图片点击后停止冒泡的过程,可以修改图片的click监听器如下:

在这里插入图片描述
执行结果如下:
在这里插入图片描述
在上图中可以看到冒泡阶段的监听器没有触发。

立即停止传播

除了 stopPropagation,还有 stopImmediatePropagation。正如它的名字所表明的那样,会立即停止事件传播,甚至阻止当前节点上的其他监听器被调用。

假设我们想要事件传播到了图片时立即停止继续传播,可以修改上面的 hanlder1 函数,把 stopPropagation 替换为 stopImmediatePropagation。执行效果如下:
在这里插入图片描述
我们可以看到绑定在图片上的第二个监听器函数并没有执行,事件传播过程被立即终止了。

事件的取消

有些事件会在传播结束后执行一些默认的浏览器操作。例如,单击一个链接或单击表单提交按钮分别会使浏览器导航到新页面和提交表单。

通过在监听器中调用event对象的另一个方法 preventDefault(),可以避免浏览器执行默认的操作。

结语

在本文中我们学习了事件冒泡和事件捕获是如何在JavaScript中工作的。如果你有任何问题或意见,欢迎留言。


http://www.ppmy.cn/news/32538.html

相关文章

【微信小程序】-- 全局数据共享 - MobX(四十三)

💌 所属专栏:【微信小程序开发教程】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &…

Mysql常用命令

mysql连接: [roothost]# mysql -u root -p Enter password:******创建数据库: CREATE DATABASE 数据库名; 删除数据库: drop database 数据库名; 使用mysqladmin删除数据库: [roothost]# mysqladmin -u root -p dr…

面试了一个32岁的程序员,一个细节就看出来是培训班的····

首先,我说一句:培训出来的,优秀学员大有人在,我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配,是非常遗憾的事情。 最近,在网上看到这样一个留言,引发了程序员这个圈子不少的…

【pygame游戏】Python实现蔡徐坤大战篮球游戏【附源码】

前言 话说在前面,我不是小黑子~😏 本文章纯属技术交流~娱乐 前几天我获得了一个坤坤打篮球的游戏,也给大家分享一下吧~ 好吧,其实并不是这样的游戏,往下慢慢看吧。 准备工作 开发环境 Python版本:3.7.8 …

响应式编程详解,带你熟悉Reactor响应式编程

文章目录一、什么是响应式编程1、Java的流和响应式流2、Java中响应式的使用3、Reactor中响应式流的基本接口4、Reactor中响应式接口的基本使用二、初始Reactor1、Flux和Mono的基本介绍2、引入Reactor依赖3、响应式类型的创建4、响应式类型的组合(1)使用m…

http协议 - 笔记

1 http协议 -- post,get,delete 如何使用http协议post - /api/v1/User/1 要使用 HTTP 协议 POST 方法向 /api/v1/User/1 发送请求,您可以使用一个 HTTP 客户端(例如 Postman、cURL 或浏览器扩展程序)并按照以下步骤操作: 打开您的 HTTP 客户端。在 URL 地址栏中输入 /a…

站上风口,文心一言任重道远

目录正式发布时机选择逻辑推理AI绘画用户选择总结自从OpenAI公司的chatGPT发布以来,吸引了全球目光,同时也引起了我们的羡慕,希望有国产的聊天机器人,盼星星盼月亮,终于等来了百度文心一言的发布。 正式发布 3月16日…

k8s学习之路 | k8s 工作负载 Job

文章目录1. Job 基础1.1 Job 资源1.2 使用场景1.3 示例 Job1.4 编写 Job1.5 一些特性2. Job 特性用例2.1 运行一次 Job2.2 自动清理完成的 Job2.3 更多特性1. Job 基础 1.1 Job 资源 Job 负载资源会创建一个或者多个 Pod,并将继续重试 Pod 的执行,直到…