前端中的事件委托

news2024/11/17 21:44:18
前端小知识
事 件 委 托

作者李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/132819265


【介绍】:本文介绍前端中的事件委托。

上一节:《 上一节标题 | 下一节:《 下一节标题


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>元素,如果是,就执行相应的操作。在本例中,我们改变了被点击的列表项的背景颜色。效果如图所示:

在这里插入图片描述

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

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

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

相关文章

MATLAB语言 实验一 MATLAB工作环境熟悉及简单命令的执行

一、 实验名称 MATLAB工作环境熟悉及简单命令的执行 二、 实验目的 熟悉MATLAB的工作环境&#xff0c;学会使用MATLAB进行一些简单的运算。 三、实验内容 MATLAB的启动和退出&#xff0c;熟悉MATLAB的桌面&#xff08;Desktop&#xff09;&#xff0c;包括菜单&#xff08…

抖音视频批量智能剪辑/智能一键成片功能如何技术开发源头?

抖音seo&#xff0c;视频剪辑&#xff0c;批量发布&#xff0c;账号矩阵管理&#xff0c;无人直播自动询盘锁定客户&#xff0c;想实现以上功能都要有正规的接口权限&#xff0c;这个权限接口已经在前面文章发过。 一、剪辑技术开发 智能剪辑&#xff1a;咱们研发公司自主研发…

初识Vue3

目录 创建实例 Vue3生命周期 响应式基础 为什么要使用 ref&#xff1f; 声明响应式状态 ref()和reactive() toRef()和toRefs() 创建实例 通过对Vue2的学习&#xff0c;我们可以这样在Vue2中创建一个实例&#xff1a; var vm new Vue({// 选项 }) 或者通过Vue全局api…

webrtc-m79-测试peerconnectionserver的webclient-p2p-demo

1 背景 webrtc的代码中有peerconnectionclient和peerconnectionserver的例子&#xff0c;但是没有对应的web端的例子&#xff0c;这里简单的写了一个测试例子&#xff0c;具体如下&#xff1a; 2 具体操作 2.1 操作流程 2.2 测试效果 使用webclient与peerconnectionclient的…

Windows安装MySQL8.0完整教程

很多朋友在安装MySQL的时候&#xff0c;总会遇到各种各样的问题。本文来教你怎样正确安装MySQL。 一、 下载MySQL 如果已经下载好了可以忽略&#xff0c;我下面提供两个版本的下载链接 阿里云盘 夸克云盘 链接&#xff1a;https://pan.quark.cn/s/1894623c2e6a 提取码&…

Go入门教程

什么是Go语言&#xff1f; Go&#xff08;又称 Golang&#xff09;是 Google 的 Robert Griesemer&#xff0c;Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近&#xff0c;但功能上有&#xff1a;内存安全&#xff0c;GC&#xff08;垃圾回…

BGP感想

BGP 边界网关协议 属于外部或域间路由协议&#xff0c;距离矢量路由协议。 AS(自治系统)&#xff0c;在一个自治系统内运行osfp,is-is,rip,vlan等,实现AS内网络互通。 BGP做什么&#xff0c;为处于不同自治系统&#xff08;AS&#xff09;中的路由器之间进行“路由信息通信…

视频推流测试——使用ffmpeg进行推流生成rtsp视频流

在我们完成开发工作之后,需要通过推流的形式来验证能否正确接收视频流,并送入视频检测程序。笔者在这里使用的是业内最为常用的ffmpeg。具体方法如下。 1、安装ffmpeg 访问ffmpeg的官网,地址为https://ffmpeg.org/download.html,按照如下途中来选择下载。 下载完成后,会…

LeetCode(力扣)53. 最大子数组和Python

LeetCode53. 最大子数组和 题目链接代码 题目链接 https://leetcode.cn/problems/maximum-subarray/ 代码 class Solution:def maxSubArray(self, nums: List[int]) -> int:result float(-inf)count 0for i in range(len(nums)):count nums[i]if count > result:res…

华为云云耀云服务器L实例评测|华为云云耀云服务器docker部署srs并调优,可使用webrtc与rtmp

华为云云耀云服务器L实例评测&#xff5c;华为云云耀云服务器docker部署srs并调优&#xff0c;可使用webrtc与rtmp 什么是华为云云耀云L实例 云耀云服务器L实例&#xff0c;面向初创企业和开发者打造的全新轻量应用云服务器。提供丰富严选的应用镜像&#xff0c;实现应用一键…

BUUCTF Reverse/[2019红帽杯]childRE

查看信息 分析代码 int __cdecl main(int argc, const char **argv, const char **envp) {__int64 v3; // rax_QWORD *v4; // raxconst CHAR *v5; // r11__int64 v6; // r10int v7; // er9const CHAR *v8; // r10__int64 v9; // rcx__int64 v10; // raxunsigned int v12; // ec…

基于人工智能与边缘计算Aidlux的工业表面缺陷检测

一&#xff1a;训练yolov8得到onnx模型&#xff08;相关教程有很多&#xff09; 二&#xff1a;模型转化&#xff1a; 网站&#xff1a; https://aimo.aidlux.com/ 输入试用账号和密码: 账号:AIMOTC001&#xff0c;密码:AIMOTC001 我们选择 TensorFlowLite 一步步完成转化 …

自然语言处理应用(二):自然语言推断

自然语言推断 自然语言推断&#xff08;Natural Language Inference&#xff09;是指通过对自然语言文本进行逻辑推理和推断&#xff0c;判断两个句子之间的关系&#xff0c;通常包括三种关系&#xff1a;蕴含&#xff08;entailment&#xff09;、矛盾&#xff08;contradict…

Java通过http请求的方式调用他人的接口

本功能的实现&#xff0c;去不参数于这篇博客&#xff0c;给这位大神点赞 基于Spring Boot使用Java调用http请求的6种方式 文章目录 业务背景第一步&#xff0c;配置url第二步&#xff0c;封装请求体&#xff0c;RequestBody第三步&#xff0c;使用HttpURLConnection调用服务…

机器学习(8)---数据预处理

文章目录 一、数据预处理1.1 数据无量纲化1.2 数据归一化1.3 数据标准化1.4 处理选择 二、缺失值2.1 填补的类和参数2.2 用Pandas和Numpy进行填补 三、处理分类型特征&#xff1a;编码与哑变量3.1 编码3.2 哑变量&#xff08;独热编码&#xff09; 一、数据预处理 1.1 数据无量…

【java】【SSM框架系列】【二】SpringMVC

目录 一、SpringMVC简介 1.1 SpringMVC概述 1.2 入门案例 1.3 入门案例工作流程分析 1.4 Conrtoller加载控制与业务BEAN加载控制 1.5 PostMan 二、请求与响应 2.1 请求映射路径 2.2 请求参数 2.2.1 Get 2.2.2 Post 2.2.3 SpringMVC解决post请求中文乱码处理 2.2.4 …

有效回文字符串(Valid palindrome)

题目描述 思路分析 代码实践 java: public class Solutation1 {//定义一个方法&#xff0c;判断是否是有效数字或者字母private static boolean isValid(char c) {//如果不是字母或者数字&#xff0c;那就返回一个flase//这里调用了Character类里面的方法return Character.i…

openwrt开启SSH远程访问与开启WEB远程访问——三种方法

openwrt 开启SSH远程访问 首先&#xff0c;你的电脑用网线连接路由器LAN口是可以访问WEB页面和SSH连接的。 例如&#xff0c;电脑1连接Openwrt路由器&#xff0c;可以进行SSH连接到openwrt 路由器。但是电脑2无法远程访问Openwrt路由器网页和SSH远程连接。 本次操作固件版本…

前端面试题JS篇(5)

如何判断一个对象是空对象 1、使用 JSON 自带的stringify方法判断 let obj{}; console.log(JSON.stringify(obj)"{}"); 2、使用 ES6 新增的方法 Object.keys()来判断 Object.keys(obj).length0 如果 new 一个箭头函数的会怎么样 箭头函数是ES6中的提出来的&…

python main 函数-启动-传递参数 python 打包 exe C# 进程传参

Part1:Python main 传递参数 在Python编程中&#xff0c;我们经常需要从命令行或其他外部环境中获取参数。Python提供了一种简单而灵活的方式来处理这些参数&#xff0c;即通过main函数传参 1.python main 函数-启动-传递参数 test.py import sysdef main():# 获取命令行参…