当前位置: 首页 > news >正文

JavaScript事件机制详解:捕获、冒泡与事件委托

目录

1.什么是JavaScript的事件机制?

2.DOM树

3.事件触发流程

4. 如何利用事件冒泡机制(事件委托)

5.如何阻止事件冒泡

6.总结


1.什么是JavaScript的事件机制?

JavaScript的事件机制一共分为三个部分,分别是事件捕获,事件冒泡事件委托。

(1)事件捕获:指的是事件从根节点(document节点)向目标事件传播的过程。

(2)事件冒泡:指的是当捕获到目标事件之后从目标事件开始向上传播(传播至Document节点)的过程。

(3)事件委托:指的是利用事件冒泡机制,在对应子元素的父元素节点上绑定事件处理器,避免对多个子元素分别绑定事件监听器。这样做可以显著减少内存占用和事件处理器的数量,提升页面性能,尤其是在子元素数量很多或动态生成的场景下,事件委托能降低浏览器的事件管理开销,提高响应效率和代码维护性。

  想要掌握好JavaScript中的事件机制,首先掌握好事件是如何被捕获的(事件捕获阶段),捕获到了事件是怎么触发的(事件执行阶段),事件触发之后会怎么样(事件冒泡阶段)。理解这些之后就知道如何利用事件冒泡来进行事件委托了。


2.DOM树

在讲述事件机制之前,首先掌握一个概念,什么是DOM树。理解了DOM树可以让我们更好的理解事件的触发和冒泡过程。

什么是DOM树?

  • DOM(文档对象模型,Document Object Model)是浏览器解析HTML后生成的一个树状结构。
  • 每个HTML标签、文本节点、属性都会被表示成DOM树上的一个节点。
  • 通过DOM树,JavaScript可以访问、修改网页内容和结构。

案例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><div id="container"><button id="btn">点击我</button></div></body>
</html>

DOM树为:

Document
 └── html
      └── body
           └── div#container
                └── button#btn (文本节点: "点击我")


3.事件触发流程

假设我们在上面HTML中给按钮和它的父元素div都绑定点击事件:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><div id="container"><button id="btn">点击我</button></div><script>const container = document.getElementById('container');const btn = document.getElementById('btn');container.addEventListener('click', () => {console.log('容器被点击');});btn.addEventListener('click', () => {console.log('按钮被点击');});</script></body>
</html>

事件触发流程:

  1. 当用户点击按钮时,浏览器根据DOM树找到事件目标(button)。
  2. 事件先从document开始捕获,经过html、body、div,最后到button(捕获阶段)。明事件捕获默认是不启用的,需要在addEventListener第三个参数设置为true才会执行捕获阶段的监听。否则事件监听默认只在目标阶段和冒泡阶段触发。
  3. 事件在button上触发(目标阶段)
  4. 事件从button向上冒泡,经过div、body、html、document(冒泡阶段)
  5. 绑定在button和container上的事件都会被触发,输出:按钮被点击 容器被点击。

从上述案例我们可以看出,当我们在给一个事件绑定了监听器之后,如果该监听器被触发了,浏览器首先需要找到目标元素(捕获阶段),找到目标元素之后浏览器会触发当前元素绑定的事件监听器的事件(目标阶段),当执行完当前的事件之后会继续沿着DOM树向上触发(冒泡阶段),由于<div id="container">是该按钮的父元素并且也绑定了事件监听,所以也会触发,直至传递到根节点。


4. 如何利用事件冒泡机制(事件委托)

假设你有一个列表,里面有很多条目,每条目都有一个“删除”按钮:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><ul id="list"><li>项目1 <button class="delete-btn">删除</button></li><li>项目2 <button class="delete-btn">删除</button></li><li>项目3 <button class="delete-btn">删除</button></li><!-- 可能还有更多项目 --></ul></body>
</html>

(1)传统做法(不使用事件委托):

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><ul id="list"><li>项目1 <button class="delete-btn">删除</button></li><li>项目2 <button class="delete-btn">删除</button></li><li>项目3 <button class="delete-btn">删除</button></li><!-- 可能还有更多项目 --></ul><script>const deleteButtons = document.querySelectorAll('.delete-btn');deleteButtons.forEach(btn => {btn.addEventListener('click', function() {const li = this.parentElement;li.remove();console.log('删除了一个项目');});});</script></body>
</html>

我们发现,如果想要点击按钮就删除对应元素,我们可以给每一个按钮都绑定事件监听器来达到效果。这样对于少部分的事件其实并没有什么影响,但是如果事件很多的情况下,绑定大量事件监听器会消耗内存和性能。而且如果后来还需要动态添加新的项目,则需要额外绑定事件。

(2)利用事件冒泡机制(事件委托)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><ul id="list"><li>项目1 <button class="delete-btn">删除</button></li><li>项目2 <button class="delete-btn">删除</button></li><li>项目3 <button class="delete-btn">删除</button></li><!-- 可能还有更多项目 --></ul><script>const list = document.getElementById('list');list.addEventListener('click', function(event) {// 利用事件冒泡,判断点击目标是否是删除按钮if (event.target && event.target.classList.contains('delete-btn')) {const li = event.target.parentElement;li.remove();console.log('删除了一个项目(事件委托)');}});</script></body>
</html>

从上述案例我们可以发现,我们只绑定了一个事件监听(ul),但是当子元素(li)触发点击事件之后,我们并没有对子元素绑定事件处理,但是也能够进行删除操作,这是因为子元素的事件会沿着DOM树向上传播,当传播到ul节点的时候触发了ul的事件处理机制。所以进行了删除。


5.如何阻止事件冒泡

当我们希望事件只在某个特定元素上响应,而不影响其父元素或祖先元素时,可以使用 event.stopPropagation() 来阻止事件冒泡。

此外,如果希望不仅阻止事件冒泡,还阻止当前元素上后续的同类型事件监听器执行,可以使用 event.stopImmediatePropagation()

区别:

  • event.stopPropagation():阻止事件继续冒泡到父元素,但当前元素上绑定的其他同类型事件监听器仍会执行。
  • event.stopImmediatePropagation():阻止事件冒泡,并且阻止当前元素上后续同类型事件监听器的执行。

案例:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8" /><title>阻止事件冒泡案例</title>
</head>
<body><div id="outer" style="padding:20px; border:1px solid #ccc;">外层容器(点击会触发)<button id="inner">点击我</button></div><script>const outer = document.getElementById('outer');const inner = document.getElementById('inner');outer.addEventListener('click', () => {console.log('【外层容器】点击事件触发');});// 监听器1:使用 stopPropagation()inner.addEventListener('click', (event) => {event.stopPropagation();  // 阻止事件冒泡,父元素的点击事件不会触发console.log('【按钮监听器1】点击事件触发,事件冒泡被阻止,父元素不会收到事件');});// 监听器2:同一元素上的另一个监听器inner.addEventListener('click', () => {console.log('【按钮监听器2】点击事件触发,仍然执行');});/*// 如果改为使用 stopImmediatePropagation(),效果如下:inner.addEventListener('click', (event) => {event.stopImmediatePropagation();  // 阻止事件冒泡,且阻止后续监听器执行console.log('【按钮监听器1】点击事件触发,事件冒泡和后续监听器执行被阻止,父元素不会收到事件');});inner.addEventListener('click', () => {console.log('【按钮监听器2】不会执行');});*/</script>
</body>
</html>

使用 stopPropagation() 时:

点击按钮会输出:

【按钮监听器1】点击事件触发,事件冒泡被阻止,父元素不会收到事件
【按钮监听器2】点击事件触发,仍然执行

使用 stopImmediatePropagation() 时:

点击按钮只会输出:

【按钮监听器1】点击事件触发,事件冒泡和后续监听器执行被阻止,父元素不会收到事件


6.总结

  • JavaScript事件机制包括三个主要阶段:

    1. 捕获阶段:事件从document节点向目标元素逐层传递,监听器在此阶段触发需设置capturetrue
    2. 目标阶段:事件到达目标元素,触发绑定在目标元素上的事件监听器。
    3. 冒泡阶段:事件从目标元素向上传播到document,触发沿途元素绑定的冒泡监听器。
  • 事件冒泡机制允许我们通过事件委托,只在父元素上绑定事件监听器,利用事件冒泡捕获子元素事件,极大优化性能和代码维护。

  • 在需要阻止事件传播时,可使用event.stopPropagation()阻止冒泡,避免父元素响应事件;使用event.stopImmediatePropagation()则进一步阻止当前元素上后续同类型监听器执行。

  • 事件监听器默认在目标阶段和冒泡阶段触发,捕获阶段监听需要显式开启

http://www.dtcms.com/a/562032.html

相关文章:

  • 网站建设哪家好知道万维科技百度成都分公司
  • 做一网站优化要多少钱新媒体营销岗位有哪些
  • 做网站 做好把我踢开推广哪个平台好
  • 在线字体设计网站云安区市场网络营销方法
  • 泉州网站制作运营商专业做网站策划案
  • 3.7 TCP拥塞控制
  • 网站建设代码南京市浦口区建设局网站
  • 电商开放平台API接口的日常实际应用
  • Agent上下文压缩之战!阿里AgentFold v.s.字节FoldAgent
  • DCRNN代码解析
  • 国内特效比漂亮的网站网站备案收费
  • 做爰试看的网站做网站的素材都在哪里下载
  • 英伟达开源了其 Aerial 软件,以加速 AI 原生 6G 的发展。
  • 网站后台慢厚街网站建设多少钱
  • 【Android】消息机制
  • 资料分析-平均数(和比重很像,可以对比学习)
  • 注解(内置注解、元注解、自定义注解)
  • nginx安装与升级
  • 开网站卖茶要怎么做设计很好的视觉很棒的网站
  • Day02计算机网络网络层学习总结:从协议到路由全解析
  • 网站建设公司 预算培训机构前端开发
  • 文献管理 Mendeley合并两个论文数据库
  • 泰兴网站推广东阳厂家高端网站设计
  • 如何利用 DeepSeek 提升工作效率-test
  • 青岛开发区做网站设计的wordpress猜你喜欢插件
  • Windows 10安装Linux虚拟机完整指南:三种方法详解
  • mysql数据库的sql优化以及explain周期字段详解案例【爽文】
  • wordpress 站点语言优秀网站h5案例分享
  • 建网站要多长时间功能最多的wordpress主题
  • 计算机图形学·5 OpenGL编程2 完整程序