JavaScript中的事件冒泡、事件捕获、事件委托

DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

在这里插入图片描述
Dom标准事件流的触发的先后顺序为:先捕获再冒泡。即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
addEventListener的第三个参数
在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件,另一个是触发事件后要执行的函数,然而addEventListener还可以传入第三个参数:

	element.addEventListener(event, function, useCapture);

第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。如果不写第三个参数则默认在事件冒泡阶段调用事件处理函数。

下面先介绍事件冒泡:

事件冒泡

事件冒泡(dubbed bubbling):当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到 window (注意这里传递的仅仅是事件,例如click、focus等等这些事件, 并不传递所绑定的事件函数。)
事件源 =>根节点(由内到外)进行事件传播。
举例说明:

在这里插入图片描述
给三个盒子依次绑定点击事件,当点击盒子的时候,会依次触发父级元素的点击事件。

	let big = document.querySelector('.big')let center = document.querySelector('.center')let small = document.querySelector('.small')big.addEventListener('click', () => {console.log('big')})center.addEventListener('click', () => {console.log('center')})small.addEventListener('click',()=>{console.log('small')})

输出如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果父元素没有绑定点击事件则只会触发点击盒子的事件。

	let big = document.querySelector('.big')let center = document.querySelector('.center')let small = document.querySelector('.small')// big.addEventListener('click', () => {//   console.log('big')// })// center.addEventListener('click', () => {//   console.log('center')// })small.addEventListener('click', () => {console.log('small')})

在这里插入图片描述
如果子元素(small)的点击事件去掉,当我们点击small的时候会把当前操作的点击事件传递给父元素(因为父元素绑定了点击函数)

	let big = document.querySelector('.big')let center = document.querySelector('.center')let small = document.querySelector('.small')big.addEventListener('click', () => {console.log('big')})center.addEventListener('click', () => {console.log('center')})// small.addEventListener('click', () => {//   console.log('small')// })

在这里插入图片描述
有些时候我们不希望产生事件冒泡,所以可以 在子事件中加入e.stopPropagation() 取消冒泡

	small.addEventListener('click', e => {e.stopPropagation() // 阻止冒泡console.log('small')})

这是时候再次点就,其他绑定了点击事件的父元素就不会再响应了
在这里插入图片描述

事件捕获

事件捕获(event capturing): 当鼠标点击或者触发dom事件时(被触发dom事件的这个元素被叫作事件源),浏览器会从根节点 =>事件源(由外到内)进行事件传播。
事件捕获与事件冒泡是比较类似的,最大的不同在于事件传播的方向。
还是举上面的例子:
tips; 因为事件冒泡是默认的,所以不做特殊处理

	let big = document.querySelector('.big')let center = document.querySelector('.center')let small = document.querySelector('.small')big.addEventListener('click', () => {console.log('big-----事件捕获')}, true)center.addEventListener('click', () => {console.log('center-----事件捕获')}, true)small.addEventListener('click', e => {// e.stopPropagation() // 阻止冒泡console.log('small-----事件捕获')}, true)big.addEventListener('click', () => {console.log('big-----事件冒泡')})center.addEventListener('click', () => {console.log('center-----事件冒泡')})small.addEventListener('click', e => {// e.stopPropagation() // 阻止冒泡console.log('small-----事件冒泡')},)

输出如下
在这里插入图片描述

事件委托

事件委托也称为事件代理。就是利用事件冒泡,把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托就无法实现。

不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

应用场景:1000个button需要注册点击事件

如果循环给每个按钮添加点击事件,那么会增加内存损耗,影响性能

	let btns = document.getElementsByTagName('button');console.log(btns);for (let i = 0; i < btns.length; i++) {btns[i].addEventListener('click', function () {console.log(i);})}

此时可以给button的父元素添加点击事件

let btnFather = document.getElementById('btnfather');
btnFather.addEventListener('click', function () {console.log(e.target.innerHTML)
}

这时相当于每个按钮都绑定了点击事件

优点:

  1. 替代循环绑定事件的操作,减少内存消耗,提高性能。比如:

     在table上代理所有td的click事件。在ul上代理所有li的click事件。
    
  2. 简化了dom节点更新时,相应事件的更新。比如:

     不用在新添加的li上绑定click事件。当删除某个li时,不用移解绑上面的click事件。
    

缺点:

  1. 事件委托基于冒泡,对于不冒泡的事件不支持。
  2. 层级过多,冒泡过程中,可能会被某层阻止掉。
  3. 理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在table上代理td,而不是在document上代理td。

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

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

相关文章

AI绘画工具汇总

目前市面上的AI绘画工具十分繁杂&#xff0c;以下工具可供参考&#xff1a; 1. Midjourney 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; Midjourney&#xff1a;最主流的AI绘图工具之一&#xff0c;出图效果好&#xff0c;简单学习就可上手。需要在di…

C++基础知识记录

github仓库不定期更新: https://github.com/han-0111/CppLearning 文章目录 C如何工作编译器和链接器编译器预处理(Preprocessing)includedefineif/endif 链接器一种比较复杂的情况 变量变量类型intcharshortlonglong longfloatdoublebool如何查看数据大小 函数头文件条件语句…

安装node.js指定任意版本详解

Node.js是一种基于Chrome V8引擎的JavaScript运行时环境。它允许开发人员使用JavaScript编写服务器端和网络应用程序。与传统的JavaScript在浏览器中执行不同&#xff0c;Node.js使得JavaScript可以在服务器端运行。 Node.js具有以下特点&#xff1a; 1. 非阻塞式I/O&#xf…

vcenter server (部署较大服务器)

作用 VMware vCenter是集中管理控制台&#xff0c;管理所有安装了VMware ESXI的主机 使用vCenter Server可以对虚拟机进行实时的监控&#xff0c;包括服务器硬件、网络和共享的存储&#xff0c;并可以进行故障诊断。 可以查看实时的统计和图表&#xff0c;监控虚拟主机和资源…

Python 中的 tqdm() 方法

tqdm&#xff08;阿拉伯语"taqaddum"的缩写&#xff0c;意为"进展"&#xff09;是Python中一个用于在循环中显示进度条的库。它提供了一种简单而又灵活的方式来监测代码执行的进度&#xff0c;特别是在处理大量数据或耗时较长的任务时非常有用。 1、安装 …

ssm045基于jsp的精品酒销售管理系统+jsp

ssm045基于jsp的精品酒销售管理系统jsp 交流学习&#xff1a; 更多项目&#xff1a; 全网最全的Java成品项目列表 https://docs.qq.com/doc/DUXdsVlhIdVlsemdX 演示 项目功能演示&#xff1a; ————————————————

Vue3中使用provide和inject依赖注入完成父组件和孙子组件之间参数传递

Vue3中使用provide和inject依赖注入完成父组件和孙子组件之间参数传递 官网介绍 注意以下写法都是使用setup 代码结构 依赖注入-父组件 import { ref, provide } from "vue"const outDialogCardInfo ref() function updateOutDialogCardInfo(item) {console.log…

Java --- JVM之StringTable

目录 一、String的基本特性 二、String的内存分配 2.1、String内存分布图 三、字符串拼接操作 3.1、字符串拼接操作底层原理 3.2、拼接操作与append操作效率对比 四、intern()方法 4.1、intern()效率 五、StringTable的垃圾回收 一、String的基本特性 1、String字符…

AWTK实现汽车仪表Cluster/DashBoard嵌入式GUI开发(七):FREERTOS移植

前言: 一般的GUI工程都需要一个操作系统,可能是linux,重量级的,也可能是FreeRTOS,轻量级的。 一句话理解那就是工程就是FreeRTOS task任务的集合。 一个main函数可以看到大框架: 很显然,除了第一个是硬件配置的初始化,中间最重要的部分就是要创建任务,把AWTK的应用…

XXX系统测试报告测试用例模板

XXX系统测试报告 编制&#xff1a; 2023-5-16 审核&#xff1a; 日期&#xff1a; 批准&#xff1a; 日期&#xff1a; 版本 修订时间 修订人 修订类型 修订章节 修订内容 *修订类型分为 A …

人工智能与大数据:驱动现代业务转型的双引擎

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;和大数据已成为驱动业务和技术创新的关键力量。它们的结合不仅重塑了传统行业&#xff0c;也催生了新的商业模式和服务方式。 AI与大数据在零售行业的应用 在零售行业&#xff0c;AI和大数据的应用已经成为提…

ai语音电销机器人电销行业要怎么降低封号率?

工信部对电话营销电话的管控越来越严格&#xff0c;企业电销行业的发展受到了很多限制&#xff0c;因为电话销售人员在进行销售工作的时候&#xff0c;经常会因为各种原因触发封号机制&#xff0c;导致手机卡号被封&#xff0c;那企业电销行业要怎么降低封号率&#xff1f; 很多…