0

0

理解JavaScript中的事件路由冒泡过程及委托代理机制

黄舟

黄舟

发布时间:2017-02-27 14:05:52

|

1240人浏览过

|

来源于php中文网

原创

当我用纯CSS实现这个以后。我开始用JavaScript和样式类来完善功能。

然后,我有一些想法,我想使用delegated events (事件委托)但是我不想有任何依赖,插入任何库,包括jquery。我需要自己实现事件委托了。

我们先来看看事件委托到底是什么?他们是怎么工作的,怎么去实现这种机制。

  好,它解决了什么问题?

  我们先看个简单的例子。

  先假设我们有一组按钮,我一次点击一个按钮,然后我希望被点中的状态设为"active"。再次点击时取消active。

  然后,我们可以写一些HTML:

立即学习Java免费学习笔记(深入)”;

  我可以用一些标准的Javascript事件处理上面的逻辑: 

var buttons = document.querySelectorAll(".toolbar .btn");
for(var i = 0; i < buttons.length; i++) {
  var button = buttons[i];
  button.addEventListener("click", function() {
    if(!button.classList.contains("active"))
      button.classList.add("active");
    else
      button.classList.remove("active");
  });
}

  看上去不错,但是它其实不能像你期望的那样工作。 

  闭包的陷阱

  如果你有一定的JavaScript开发经验,这个问题就很明显了。

  对于外行来说button变量是被封闭的,每次都会找到对应的button……但是其实这里只有一个button;每次循环都会被重新分配。

  第一个循环它指向第一个button,接下来是第二个。但当你点击时button变量永远只指向最后一个button元素,问题出在这。

  我们需要的是一个稳定的作用域;让我们重构一下。

var buttons = document.querySelectorAll(".toolbar button");
var createToolbarButtonHandler = function(button) {
  return function() {
    if(!button.classList.contains("active"))
      button.classList.add("active");
    else
      button.classList.remove("active");
  };
};

for(var i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i]));
}

  注* 上面这段代码结构有点复杂,也可以简单直接地使用一个闭包,封闭保存当前的button变量,如下所示:

var buttons = document.querySelectorAll(".toolbar .btn");

for(var i = 0; i < buttons.length; i++) {
  (function(button) {
    button.addEventListener("click", function() {
      if(!button.classList.contains("active"))
        button.classList.add("active");
      else
        button.classList.remove("active");
    });
  })(buttons[i])
}

  现在它能正常工作了。指向永远是正确的button 

  那么这个方案有什么问题?

  这个方案看上去还可以,然而我们确实可以做得更好。

  首先我们创建了太多的处理函数。为每一个匹配的.toolbar button绑定了一个事件侦听和一个回调处理。假如只有三个按钮这种资源分配是可以忽略的。

  然而,如果我们有1000个呢?

  • // ... 997 more elements ...

  它也不会崩溃,但是这并不是最佳的方案。我们分配了大量不必要的函数。让我们重构一下,仅附加一次,即仅绑定一个函数(function),去处理这种有可能的数千次调用。

  相对于封闭button变量去存储当时我们点击的对象,我们可以使用event对象去获取当时点击的对象。

  event对象有一些元数据,在多次绑定的种情况下,我们可以使用currentTarget获取当前绑定的对象,如上例的代码就可以改成:

var buttons = document.querySelectorAll(".toolbar button");

var toolbarButtonHandler = function(e) {
  var button = e.currentTarget;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
};

for(var i = 0; i < buttons.length; i++) {
  button.addEventListener("click", toolbarButtonHandler);
}

  不错!不过这只是简化了单个函数,让它得更具可读性,然而它还是被绑定了多次。

  但是,我们还可以做得更好。

  让我们假设一下,我们在这个列表里动态地添加了一些按钮。然后我们还要为这些动态元素添加和移除事件绑定。然后我们还要持久化这些处理函数和当前上下文要用到的变量,这事听上去就不靠谱。

笔头写作
笔头写作

AI为论文写作赋能,协助你从0到1。

下载

  也许还有其他方法。

  让我们先全面理解一下事件的工作原理,以及他们在DOM里是怎样传递的。

  事件的工作原理

  当用户点击一个元素时,一个事件就会被产生去通知用户当前的行为。事件在分发派遣时会有三个阶段:

  • 捕获阶段: Capturing 

  • 触发阶段: Target

  • 冒泡阶段: Bubbling

  这个事件起始从document之前然后一路向下找到当前事件点击到的对象。当事件达到点击到的对象之后,它会按原路返回(冒泡过程),直到退出整个DOM树。

  这里是一个HTML的例子:



  

当你单击Button A时,事件经过的路径会向下面这样:

START | #document  \ | HTML        | | BODY         } CAPTURE PHASE | UL          | | LI#li_1    / | BUTTON     

  注意,这意思着你可以在事件的经过路径上捕获到你单击所产生的事件,我们非常确定这个事件一定会经过他们的父元素ul元素。我们可以将我们的事件处理绑定到父元素上面,然后简化我们的解决方案,这个就叫事件的委托及代理(Delegated Events)。

  注* 其实Flash/Silverlight/WPF开发的事件机制是非常近似的,这里有一张他们的事件流程图。 除了Silverlight 3使用了旧版IE的仅有冒泡阶段的事件模型外,基本上也都有这三个阶段。(旧版IE和SL3的事件处理只有一个从触发对象冒泡到根对象的过程,可能是为了简化事件的处理机制。)

  事件委托代理

  委托(代理)事件是那些被绑定到父级元素的事件,但是只有当满足一定匹配条件时才会被挪。

  让我们看一个具体的例子,我们看看上文的那个工具栏的例子:

  因为我们知道单击button元素会冒泡到UL.toolbar元素,让我们将事件处理放到这里试试。我们需要稍微调整一下:

var toolbar = document.querySelector(".toolbar");
toolbar.addEventListener("click", function(e) {
  var button = e.target;
  if(!button.classList.contains("active"))
    button.classList.add("active");
  else
    button.classList.remove("active");
});

  这样我们清理了大量的代码,再也没有循环了。注意我们使用了e.target代替了之前的e.currentTarget。这是因为我们在一个不同的层次上面进行了事件侦听。

  • e.target 是当前触发事件的对象,即用户真正单击到的对象。

  • e.currentTarget 是当前处理事件的对象,即事件绑定的对象。

  在我们的例子中e.currentTarget就是UL.toolbar。

  注* 其实不止事件机制,在整个UI构架上FLEX(不是Flash) /Silverlight /WPF /Android的实现跟WEB也非常相似,都使用XML(HTML)实现模板及元素结构组织,Style(CSS)实现显示样式及UI,脚本(AS3,C#,Java,JS)实现控制。不过Web相对其他平台更加开放,不过历史遗留问题也更多。但是几乎所有的平台都支持Web标准,都内嵌有类似WebView这样的内嵌Web渲染机制,相对各大平台复杂的前端UI框架和学习曲线来说,使用Web技术实现Native APP的前端UI是非常低成本的一项选择。

  

 以上就是理解JavaScript中的事件路由冒泡过程及委托代理机制的内容,更多相关内容请关注PHP中文网(www.php.cn)!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

14

2026.01.29

clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址
clawdbot龙虾机器人官网入口 clawdbot ai官方网站地址

clawdbot龙虾机器人官网入口:https://clawd.bot/,clawdbot ai是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

7

2026.01.29

Golang 网络安全与加密实战
Golang 网络安全与加密实战

本专题系统讲解 Golang 在网络安全与加密技术中的应用,包括对称加密与非对称加密(AES、RSA)、哈希与数字签名、JWT身份认证、SSL/TLS 安全通信、常见网络攻击防范(如SQL注入、XSS、CSRF)及其防护措施。通过实战案例,帮助学习者掌握 如何使用 Go 语言保障网络通信的安全性,保护用户数据与隐私。

8

2026.01.29

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

545

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

191

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

327

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

11

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

16

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

10

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号