js的事件冒泡、捕获、委托

事件不仅存在js中,也存在在其他语言中,js事件背后的主要思想是能够在特定事件发生时运行代码。

先普及一个概念,什么是事件处理程序?

事件处理程序就像一个特殊的通用遥控器,可以执行某些操作,例如更改电视频道、提高空调温度以及更改家中照明的状态。但他们需要专业人员来处理附在其上的特殊按钮。

上面的类比解释了 JavaScript 中事件处理的概念。遥控器代表您正在使用的应用程序。应用程序上类似 HTML 元素的按钮是附加到遥控器上的特殊按钮。而处理特殊按钮的专业人士就是JavaScript。

JavaScript 中的事件处理使应用程序和网页具有响应性和交互性。他们通过定义和管理事件并在这些事件发生时执行特定操作来实现这一点。

事件处理程序是专门定义用于响应 JavaScript 中的特定事件的任何函数或方法。它还负责在特定事件发生时执行代码。

  1. 一个事件由不同的操作(点击、鼠标移动、事件间隔);
  2. 每当事件发生时,js就会创建一个事件对象。该对象具有属性和方法,并提供事件的详细信息;
  3. 可以使用js中的事件处理程序设置一个在事件发生时执行的函数;
  4. 每当触发事件时,都会自动创建事件对象。因此该对象会作为参数传递给事件处理函数;
  <button id="button"> click me </button><script>var btn = document.getElementById('button')btn.addEventListener('click', (e) => {console.log('event type', e.type)console.log('target Element', e.target)})</script>
关于事件冒泡、事件捕获、事件委托

点名中心:事件委托是一种技术,冒泡是事件本身所做的事情,捕获是对不冒泡的事件使用事件委托的一种方法。

DOM事件流(event flow)存在三个阶段:捕获阶段、目标阶段、冒泡阶段
在这里插入图片描述

捕获阶段:
当嵌套在各个元素中的元素被单击时发生。在单击到达最终的目的地之前,必须触发其每个父元素的单击事件。此阶段从DOM树的顶部向下渗透到目标元素。
目标阶段:
当完成捕获阶段后立即开始的阶段,这个阶段基本上就是捕获阶段结束和冒泡阶段的开始。
目标元素是事件最初发生的元素。例如:如果网页上有提交表单,则表单元素就是提交事件的目标元素。如果网页上的按钮上有事件处理程序,则该按钮是事件处理程序的目标元素。
按钮元素上注册的事件处理程序被视为目标元素。
冒泡阶段:
冒泡阶段和捕获阶段恰好相反。在此阶段,事件将目标元素通过其父元素(祖先)向上冒泡到全局窗口对象。默认情况下,您添加到所有事件都addEventListener处于冒泡阶段。
在这里插入图片描述

什么是事件冒泡
事件开始是触发.child它的元素。然后,它向上冒泡到其每个父元素,直到到达该html元素。
以表单字段为例,事件将向上冒泡到父表单,然后是表单所在的任何容器或 div,然后是body,然后是html元素,然后是document,然后是window。
当这些父元素冒泡时,任何监听器都会被触发。
事件冒泡的工作原理
DOM在目标元素处理完事件后,事件开始在层次结构中向上冒泡。然后事件从目标元素传播到文档的根。它根据所有的祖先元素在冒泡阶段的嵌套方式来遍历它们。
在事件处理程序的执行阶段,所有在祖先元素上注册的事件处理程序都会被执行。这样可以轻松捕获DOM层次结构各个阶段的事件。
可以使用stopPropagation()事件对对象上的方法来停止此操作。当您的目标是防止触发祖先元素上捕获到的事件处理程序时,它非常有用。
通过冒泡,可以分层方式处理事件。通过在父元素或容器上设置事件处理程序,您可以处理来自多个子元素上的事件。
事件冒泡可以轻松地实现事件委托。
在这里插入图片描述

例子:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="style.css" /><title>Practice</title></head><body><h1>Bubbling and Capturing phase</h1><div><button class="child">click me</button></div><script>const parent = document.querySelector("div");const child = document.querySelector(".child");parent.addEventListener("click", function () {console.log("clicked parent");});child.addEventListener("click", function () {console.log("clicked child");});</script></body>
</html>

代码说明:上面是一段 HTML 和 JavaScript 代码。

在 body 元素内部,我们有h1、div和button元素。div 是按钮元素的父元素。我们为按钮元素指定了 child 的类名。

在 JavaScript 部分,我们为父元素和子元素创建了变量。然后我们向父元素和子元素添加事件侦听器。

因此,当单击按钮时,首先调用被单击的子级。这意味着父元素中的函数在子元素中的函数之后执行。

什么是事件捕获

单击嵌套元素时会发生事件捕获。其父元素的点击事件必须在嵌套元素的点击之前触发。此阶段从树的顶部向下滴落DOM到目标元素。
addEventListener只有将的第三个参数设置为Boolean true值(默认值为false)后,事件捕获才会发生。
对于不冒泡的事件,请将其设置true为无论如何都捕获事件。
每当第三个参数addEventListener设置为true时,事件处理程序就会在捕获阶段自动触发。这样,当层次结构中的嵌套元素上发生事件时,将首先执行附加到祖先元素的事件处理程序DOM。这与默认情况不同,默认情况下事件处理程序是在冒泡阶段触发的。
在这里插入图片描述

重点代码示例:此示例其实已经涵盖了这篇文章的中心
<!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>
<body><!-- 事件捕获 --><ul id="list"><li id="listLi">1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><script>const list = document.getElementById('list');/*** 将点击事件都委托在ul标签上*/list.addEventListener('click', function(e) {// e.stopPropagation()console.log(e.target, 'click list')}, false)/*** 先执行子元素的click,再执行父元素的click*/listLi.addEventListener('click', function(e) {/*** 设置这个方法,就不会再执行父元素的click事件,因为阻止了事件冒泡,所以要想让父元素上获取到,就需要在父元素的监听的click事件上的第三个参数设置为true,* 注意:第三个参数设置为true了,那么会先执行父元素的click事件,然后才会执行子元素的点击事件。* 那么既然li#listLi已经委托给ul的标签上执行click事件了,然后我在li#listLi上再次绑定了click事件,我设置了stopPropogation,让父标签不进行数据捕获,那么我如何*/// e.stopPropagation();/*** 纵观此示例的代码,li#listLi上绑定了多个click事件,使用stopImmediatePropagation这个方法,就是为了解决li#listLi上绑定了多个click事件的多次执行问题,但是不影响ul标签的委托事件执行。它仅此与一个标签上绑定的事件以及停止冒泡等功能*/e.stopImmediatePropagation()console.log(e, 'click list li')})listLi.addEventListener('click', function(e) {/*** 设置这个方法,就不会再执行父元素的click事件,因为阻止了事件冒泡,所以要想让父元素上获取到,就需要在父元素的监听的click事件上的第三个参数设置为true,* 注意:第三个参数设置为true了,那么会先执行父元素的click事件,然后才会执行子元素的点击事件。* 那么既然li#listLi已经委托给ul的标签上执行click事件了,然后我在li#listLi上再次绑定了click事件,我设置了stopPropogation,让父标签不进行数据捕获,那么我如何*/// e.stopPropagation();console.log(e, 'click list li')})listLi.addEventListener('click', function(e) {/*** 设置这个方法,就不会再执行父元素的click事件,因为阻止了事件冒泡,所以要想让父元素上获取到,就需要在父元素的监听的click事件上的第三个参数设置为true,* 注意:第三个参数设置为true了,那么会先执行父元素的click事件,然后才会执行子元素的点击事件。* 那么既然li#listLi已经委托给ul的标签上执行click事件了,然后我在li#listLi上再次绑定了click事件,我设置了stopPropogation,让父标签不进行数据捕获,那么我如何*/// e.stopPropagation();console.log(e, 'click list li')})</script>
</body>
</html>

stopPropagation:
该stopPropagation()方法防止调用同一事件的传播。
传播意味着向上冒泡到父元素或向下捕获到子元素。

什么是事件委托

事件委托是一种侦听事件的技术,您可以将父元素委托为其中发生的所有事件的侦听器。
使用监听addEventListener点击document作为示例,但它可以是页面上的任何元素。
JavaScript 中的事件委托可以更轻松地管理和处理多个子元素上的事件。它利用DOM(文档对象模型)冒泡事件。这意味着在祖先元素上设置事件侦听器可以让您有效地处理事件。与在触发事件的单个元素上设置事件侦听器不同。回想一下,在冒泡阶段,子元素上的事件会上升到其父元素。
单个绑定的监听点击事件,示例:这种方法比较笨,可以说这种使用办事效率差

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="style.css" /><title>Practice</title></head><body><ul><li>Orange</li><li>Banana</li><li>Potato</li><li>Apple</li></ul><script>const li = document.querySelectorAll("li");li.forEach(function (list) {list.addEventListener("click", function () {alert("Yes, I click");});});</script></body>
</html>

在上面的代码中,事件侦听器被添加到每个listItems. 然后,每当点击它们时,就会弹出警报。传统上,这不是坏代码,但它违背了事件委托的目的。在下面的代码中,您将看到事件委托的重要性。

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="style.css" /><title>Practice</title></head><body><h1>Bubbling and Capturing phase</h1><ul><li>Kamal</li><li>Lawal</li><li>Olaide</li><li>Ayinde</li></ul><script>const ul = document.querySelector("ul");ul.addEventListener("click", function () {event.target.classList.toggle("highlight");});</script></body>
</html>

与初始代码不同,listItems 的父级ul将在此处处理该事件。所以父级会将我们想要实现的逻辑委托给子级。回想一下listItems树上的气泡DOM。
但在某些情况下,几乎不可能委托给子元素。例如,如果子元素本身具有嵌套子元素,则这是不可能的。在这种情况下,不可能委托给子元素。
下面的代码示例向您展示了为什么无法委托给子元素:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="style.css" /><title>Practice</title></head><body><h1>Bubbling and Capturing phase</h1><ul><li><h2>Orange</h2><p>Kamal</p></li><li><h2>Apple</h2><p>Lawal</p></li><li><h2>Banana</h2><p>Olaide</p></li></ul><script>const ul = document.querySelector("ul");ul.addEventListener("click", function () {event.target.classList.toggle("highlight");});</script></body>
</html>

上面的代码不会产生预期的输出。这是因为在我们的代码中,我们使用event.target.
在 JavaScript 中,是被点击的event.target实际元素。DOM在本例中,三种可能的结果是li、h2和p。下面的代码展示了这个问题的解决方案:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><link rel="stylesheet" href="style.css" /><title>Practice</title></head><body><h1>Bubbling and Capturing phase</h1><ul><li><h2>Orange</h2><p>Kamal</p></li><li><h2>Apple</h2><p>Lawal</p></li><li><h2>Banana</h2><p>Olaide</p></li></ul><script>const ul = document.querySelector("ul");ul.addEventListener("click", function () {event.target.closest("li").classList.toggle("highlight");});</script></body>
</html>

为了得到选择listItem的预期结果,我们将DOM遍历方法与event.target.
该closest方法可用于DOM对象。closest在祖先树中向上遍历。您可以通过 ID、标签或类名使用任何 CSS 选择器选择最接近的元素。一件有趣的事情是,其中closest包括您调用它的元素,在本例中为li.

e.preventDefault()

用于执行操作时停止浏览器的默认行为。
例如:

  1. 单击复选框/无线电 输入选择/取消选择复选框
  2. 单击输入/文本区域字段会聚焦输入并将光标置于输入元素中

event.stopPropagation()

此方法用于防止浏览器中事件流的捕获/冒泡阶段中定义的事件传播。

event.stopImmediatePropagation()

如果我们在单个 HTML 元素上有多次单击侦听器怎么办?
例如:代码示例的运行

<script> $("div").click(function(event) { event.stopImmediatePropagation(); alert('首次点击触发'); }); $("div").click(function(event) { // 该函数不会被执行alert('第二次点击触发'); }); 
</script>
<div>点我</div>

单击 div 元素将:

  • 防止事件冒泡到父元素
  • 阻止执行附加到该元素的任何其他事件侦听器
    stopImmediatePropagation= stopPropagation+(删除了其他事件侦听器

参考:

带你理解DOM事件流
freecodecamp 事件冒泡和捕获
gomakethings 事件委托、冒泡和捕获的区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java自动化-03-04java基础之数据类型举例

1、需要特殊注意的数据类型举例 1&#xff09;定义float类型&#xff0c;赋值时需要再小数后面带f float num11.2f; System.out.println(num1);2&#xff09;定义double类型&#xff0c;赋值时直接输入小数就可以 3&#xff09;另外需要注意&#xff0c;float类型的精度问题…

鸿蒙开发就业前景到底怎么样?

随着科技的不断进步&#xff0c;鸿蒙操作系统的推出为开发者们带来了新的机遇和挑战。鸿蒙&#xff0c;作为华为自主研发的操作系统&#xff0c;旨在为消费者提供更为流畅、安全的智能设备体验。那么&#xff0c;鸿蒙开发就业前景如何呢&#xff1f; 一、鸿蒙操作系统的优势 …

探索--------------redis缓存三大问题及解决方案

目录 一、redis的三大缓存问题 1、缓存穿透 1.1 问题描述 1.2缓存穿透发生的条件 1.3缓存穿透发生的原因 1.4解决方案 2、缓存雪崩 2.1问题描述 2.2解决缓存雪崩问题的方法有&#xff1a; 3、缓存击穿 &#xff08;热点数据集中失效&#xff09; 3.1问题描述 3.2缓…

SpringBoot快速入门笔记(3)

文章目录 一、MybatisPlus1、ORM2、添加依赖3、全局配置4、Navicat5、UserController6、CRUD操作7、BaseMapper8、两个注解 二、多表查询1、模拟用户订单2、通过用户查相关订单3、UserMapperNew4、查询订单和所属用户5、OrderMapper6、OrderController 三、条件查询四、分页查询…

【Ubuntu】用 VMware 安装 macOS

本教程使用 Ubuntu 20.04.6 LTS&#xff0c;VMware Workstation Pro 17.5.1&#xff0c;macOS Sonoma 14.4。文中所有需要的下载链接均以 Markdown 的形式体现在文字上。 下载 VMware Workstation Pro&#xff0c;目前最新版本是 17.5.1。 使用密钥&#xff0c;进行破解。 VM…

金融中的数学知识

随机偏微分方程相比普通偏微分方程具有额外的随机项&#xff0c;反映了其描述的现象具有随机性质

【核弹级安全事件】XZ Utils库中发现秘密后门,影响主要Linux发行版,软件供应链安全大事件

Red Hat 发布了一份“紧急安全警报”&#xff0c;警告称两款流行的数据压缩库XZ Utils&#xff08;先前称为LZMA Utils&#xff09;的两个版本已被植入恶意代码后门&#xff0c;这些代码旨在允许未授权的远程访问。 此次软件供应链攻击被追踪为CVE-2024-3094&#xff0c;其CVS…

中国大学生计算机设计大赛—软件应用与开发赛道—赛后感想

1.比赛介绍 中国大学生计算机设计大赛是我国高校面向本科生最早的赛事之一&#xff0c;是全国普通高校大学生竞赛排行榜榜单赛事之一。自2008年开赛至2019年&#xff0c;一直由教育部高校与计算机相关教指委等或独立或联合主办。大赛的目的是以赛促学、以赛促教、以赛促创&…

一些题目学习

1.打开文件添加helloworld public class Saier {public static void main(String[] args){String path"C:\\Users\\sjg\\Desktop\\abc.txt";String text"hello world";try {File file new File(path);FileWriter fileWriter new FileWriter(file,true);…

阴影画图转html

深受启发 https://segmentfault.com/a/1190000014943400?utm_sourcetag-newest https://gitee.com/yun-36/shadow-drawing 通过File对象&#xff0c;读成dataURL&#xff0c;生成图片&#xff0c;挂到canvas&#xff0c;生成图片文件对应的rgba数据像素点信息&#xff0c;处理…

ideaSSM 校园兼职招聘平台bootstrap开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea 开发 SSM 校园兼职招聘平台是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff…

探索设计模式的魅力:揭秘B/S模式在AI大模型时代的蜕变与进化

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;揭秘B/S…

小波降噪基础-python版本

这篇小文将使用小波多分辨分析对一个简单信号进行降噪&#xff0c;主要是降噪流程&#xff0c;为以后的小波更复杂的降噪算法打下良好的基础。降噪算法流程大致如下&#xff1a; &#xff08;1&#xff09;去趋势项&#xff08;如直流电流&#xff09;&#xff0c;并将数据归一…

不能在主机和虚拟机之间拷贝文本(虚拟机ubuntu16.04)

问题 ubuntu16.04不能在主机和虚拟机之间拷贝文本。 原因 vmware tools没安装好。 解决办法 重新安装vmware tools&#xff0c;步骤入下&#xff1a; 让虚拟机加载C:\Program Files (x86)\VMware\VMware Workstation\linux.iso光盘文件&#xff0c;设置如下&#xff1a; …

代码随想录算法训练营第30天|LeetCode236.二叉树的最小公共祖先

代码随想录算法训练营第30天|LeetCode236.二叉树的最小公共祖先 1、LeetCode236.二叉树的最小公共祖先 236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 自底向上查找&#xff0c;有点难度&#xff01; | LeetCode&#xff1a;236. 二叉树的最近公共祖先_哔…

layui框架实战案例(26):layui-carousel轮播组件添加多个Echarts图标的效果

在Layui中&#xff0c;使用layui-carousel轮播组件嵌套Echarts图表来实现多个图表的展示。 css层叠样式表 调整轮播图背景色为白色&#xff1b;调整当个Echarts图表显示loading…状态&#xff1b;同一个DIV轮播项目添加多个Echarts的 .layui-carousel {background-color: #f…

Redis 缓存雪崩、穿透、击穿、预热

在实际工程中&#xff0c;Redis 缓存问题常伴随高并发场景出现。例如&#xff0c;电商大促、活动报名、突发新闻时&#xff0c;由于缓存失效导致大量请求访问数据库&#xff0c;导致雪崩、击穿、穿透等问题。因此&#xff0c;新系统上线前需预热缓存&#xff0c;以应对高并发&a…

文献阅读:将条形码神经解剖学与空间转录分析相结合,可以识别投射神经元相关基因

文献介绍 「文献题目」 Integrating barcoded neuroanatomy with spatial transcriptional profiling enables identification of gene correlates of projections 「研究团队」 Anthony M. Zador&#xff08;美国冷泉港实验室&#xff09; 「发表时间」 2021-05-10 「发表期…

# #一眼就解密

BUUCTF #一眼就解密 1 下面的字符串解密后便能获得flag&#xff1a;ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30 注意&#xff1a;得到的 flag 请包上 flag{} 提交 flag{THE_FLAG_OF_THIS_STRING}

MPLS-基础、LSR、LSP、标签、体系结构

MPLS技术 MPLS基础 MPLS&#xff1a;转发数据时&#xff0c;只在网络边缘分析IP报文头&#xff0c;不在每一跳都分析&#xff0c;节约了转发时间。 MPLS&#xff1a;Multiprotocol Label Switching&#xff0c;多协议标签交换骨干网技术。主要应用&#xff1a;VPN、流量工程…