前端中的事件委托

简介: 前端中的事件委托

前端小知识事 件 委 托


1. 引例

考虑下面一个问题:

  • 当我们在前端开发中遇到需要 为一组相似的元素绑定事件处理程序 的情况时,通常会面临一个问题:为每个元素都创建一个独立的事件处理函数——这种方式是否是好?

我们将通过下面这个例子来说明这个问题和委托的概念。

【问题描述】:

  • 假设我们有一个网页,其中包含一个购物车,里面有很多商品项,每个商品项都有一个"添加到购物车"按钮。
  • 我们希望当用户点击这些按钮时能够执行相同的操作,即将商品添加到购物车,并更新购物车的显示。

一种最简单的方式是为每个按钮分别绑定点击事件处理程序,就像这样:

<ul id="cart">
  <li>
    <span>商品1</span>
    <button class="add-to-cart">添加到购物车</button>
  </li>
  <li>
    <span>商品2</span>
    <button class="add-to-cart">添加到购物车</button>
  </li>
  <!-- 更多商品项... -->
</ul>
<script>
const addToCartButtons = document.querySelectorAll('.add-to-cart');
addToCartButtons.forEach(button => {
  button.addEventListener('click', function() {
    // 执行添加到购物车的逻辑
    // 更新购物车显示
  });
});
</script>

尽管上述代码在功能上是可行的,但它存在一个潜在问题:

  • 为每个按钮都创建一个单独的事件处理函数。
  • 可以想象——当有大量商品时会导致创建大量相似的函数实例,占用大量内存。

2. 事件冒泡

事件冒泡(Event Bubbling)是指在处理DOM事件时,事件会从触发它的最内层元素开始冒泡,逐级向上传播,一直传递到根元素(通常是或),直到被停止或取消。这意味着如果一个子元素触发了某个事件,那么这个事件也会逐级传递给该子元素的父元素,以及父元素的父元素,以此类推。

事件冒泡的工作流程如下:

  1. 首先,用户在页面上的某个元素上触发了一个事件,比如点击鼠标或触摸屏幕;
  2. 事件首先被分派到触发事件的 最内层元素(目标元素),并在该元素上 执行绑定的事件处理程序
    3.然后,事件开始冒泡,向上级元素传递,一层一层地触发父元素上的相同事件;
    4.当事件到达文档根部(通常是元素)时,它可能会 停止冒泡,也可能继续传递到浏览器层面,(根据事件是否被取消来决定)。

在这个过程中,任何父元素上绑定的事件处理程序都可能被触发,包括目标元素本身的父元素、爷爷元素、曾祖父元素,以及文档根元素等。

3. 事件冒泡是委托的实现依据

在第1小节的案例中,我们提到了一个小缺陷。也iu是,为每个按钮都创建一个单独的事件处理函数,可能导致大量商品时会导致创建大量相似的函数实例,占用大量内存。

这时,我们联想起冒泡机制:

  • 冒泡机制使得事件 可以在DOM结构中传递并被多个元素捕获。这允许我们 在父元素上捕获子元素的事件,从而减少事件处理程序的数量,提高性能和可维护性。

事实上,冒泡机制也是事件委托模式的基础。

结合第1小节的案例来看,当用户点击任何一个"添加到购物车"按钮时,事件会冒泡到

  • 元素,我们可以在父元素上处理事件,修改被修改的那部分代码如下:
<script>
const cart = document.getElementById('cart');
cart.addEventListener('click', function(event) {
  if (event.target.classList.contains('add-to-cart')) {
    // 执行添加到购物车的逻辑
    // 更新购物车显示
  }
});
</script>

修改后的脚本相比于第1小节中的脚本的主要不同之处在于——事件处理的位置。

在第1小节中,事件处理程序是直接绑定在每个 “添加到购物车” 按钮上,使用了 button.addEventListener。这意味着每个按钮都有自己的独立事件处理程序,当用户点击其中一个按钮时,只会触发与该按钮关联的事件处理程序:

addToCartButtons.forEach(button => {
  button.addEventListener('click', function() {
    // 执行添加到购物车的逻辑
    // 更新购物车显示
  });
});

而修改后的脚本,事件处理程序是绑定在购物车容器

  • 上,使用了 cart.addEventListener。然后,在事件处理程序内部,通过检查 event.target来确定触发事件的元素是否包含类名 ‘add-to-cart’。如果是,就执行相应的逻辑:
const cart = document.getElementById('cart');
cart.addEventListener('click', function(event) {
  if (event.target.classList.contains('add-to-cart')) {
    // 执行添加到购物车的逻辑
    // 更新购物车显示
  }
});

这样修改有什么好处呢:

  • 前者每个按钮都有自己的事件处理程序,这在逻辑上更容易理解,但当有大量按钮时,会导致创建多个函数实例,可能占用更多内存。
  • 后者只有一个事件处理程序绑定在购物车容器上,这可以减少内存占用,提高性能,因为只有一个事件处理程序实例。

后者这种利用事件的冒泡特性,将多个子元素的同一类型的监听器合并到父元素上来实现的方式就被称作所谓的事件委托。

4. 另一个场景:动态生成的元素的事件绑定

事件委托的思想还可以应用于 监听 动态生成的元素和动态绑定事件。

动态生成的元素 是指 在页面加载后 通过JavaScript代码创建的元素——由于这些元素并不存在于初始HTML中,因此不能像静态元素那样直接绑定事件处理程序

让我们通过一个具体的例子来详细讲解——如何使用事件委托来监听和处理动态生成的元素的事件。

假设我们有一个按钮,点击它将在页面上动态创建一个新的列表项(<li>元素),然后我们希望能够点击这些动态创建的列表项并执行一些操作,比如改变它们的样式或进行其他操作。代码如下。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>动态元素事件委托案例</title>
</head>
<body>
  <ul id="dynamic-list">
    <!-- 这里没有任何<li>元素 -->
  </ul>
  <button id="add-button">添加列表项</button>
  <script>
    const dynamicList = document.getElementById('dynamic-list');
    const addButton = document.getElementById('add-button');
    // 点击按钮时,动态创建一个新的列表项
    addButton.addEventListener('click', function() {
      const newItem = document.createElement('li');
      newItem.textContent = '新的列表项';
      dynamicList.appendChild(newItem);
    });
    // 事件委托,监听<ul>上的点击事件
    dynamicList.addEventListener('click', function(event) {
      if (event.target.tagName === 'LI') {
        // 当点击列表项时,执行操作
        event.target.style.backgroundColor = 'lightblue';
      }
    });
  </script>
</body>
</html>

本例中,有一个 添加按钮(addButton)和一个空的 无序列表(dynamicList)。

  • 当用户点击按钮时,动态创建一个新的列表项(<li>元素),并将其添加到列表中。
  • 我们使用事件委托将点击事件绑定到<ul>元素上,监听了所有<li>元素的点击事件。

无论何时点击动态生成的列表项,事件都会冒泡到<ul>元素,事件处理程序检查被点击的元素是否是<li>元素,如果是,就执行相应的操作。在本例中,我们改变了被点击的列表项的背景颜色。效果如图所示:

从本例可以看到,这种方式下,我们能够动态绑定事件处理程序而不需要为每个列表项都手动绑定——因此,无论有多少个列表项,代码都能完成处理。

目录
相关文章
|
3月前
|
前端开发 JavaScript 开发者
掌握Web前端事件处理精髓:从事件冒泡到事件委托,轻松优化你的交互体验与代码性能!
【8月更文挑战第23天】在Web前端开发中,事件处理是实现用户交互的关键机制。其中,事件冒泡与事件委托是优化页面性能、简化代码的重要手段。事件冒泡是指事件从触发它的元素开始,沿着DOM树向上逐层传播至根节点的过程。通过阻止事件冒泡,可以控制事件的影响范围。而事件委托则是利用事件冒泡特性,在父元素上设置监听器来响应子元素的事件,这种方法减少了监听器的设置数量,特别适用于动态添加的子元素,提高了代码的可维护性和性能。掌握这两种技术,能帮助开发者构建更高效、更简洁的应用程序。
54 0
|
6月前
|
前端开发 JavaScript
【Web 前端】什么是事件冒泡?什么是事件委托?
【4月更文挑战第22天】【Web 前端】什么是事件冒泡?什么是事件委托?
|
前端开发 JavaScript 容器
前端小知识:事件委托及其应用
本文通过实际案例介绍前端中的事件委托及其应用。
85 0
|
前端开发 JavaScript 开发者
前端祖传三件套JavaScript的DOM之事件的事件委托
在前端开发中,事件处理是非常重要的一部分。JavaScript 中的 DOM 事件机制提供了丰富的功能和灵活的方式来处理各种事件,并且事件委托是其中不可或缺的一部分。本文将介绍 JavaScript 中的事件委托。
118 1
|
前端开发 JavaScript
#yyds干货盘点# 前端歌谣的刷题之路-第一百六十一题-事件委托
#yyds干货盘点# 前端歌谣的刷题之路-第一百六十一题-事件委托
86 0
#yyds干货盘点# 前端歌谣的刷题之路-第一百六十一题-事件委托
|
JavaScript 前端开发 索引
手撕前端面试题(Javascript~事件委托、数组去重、合法的URL、快速排序、js中哪些操作会造成内存泄漏......
手撕前端面试题(Javascript~事件委托、数组去重、合法的URL、快速排序、js中哪些操作会造成内存泄漏......
158 0
手撕前端面试题(Javascript~事件委托、数组去重、合法的URL、快速排序、js中哪些操作会造成内存泄漏......
|
JavaScript 前端开发
中高级前端应该必会,js实现事件委托代理、切换样式、元素获取相对于文档位置等
1、介绍   随着组件开发大流行,现在三大框架已经基本占领了整个前端。   这时候,我们要是引入一个 jq 是不是先得你的项目非常臃肿,jq 也很不适合。   这个时候,你就需要来增加你 js 的功底。
1488 0
|
30天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
127 2
|
30天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
37 0
|
30天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。