我们在学习JavaScript中,难免都会去网上查一些资料。也许偶尔就会遇到“事件委托”(也有的称我“事件代理”,这里不评论谁是谁非。以下全部称为“事件委托”),尤其是在查JavaScript的事件处理的时候。但是,大多数时说的是“事件绑定”,对于“事件委托”,或是不提,或是浅尝辄止。对于我这个比较好奇的人来说,实在很蛋疼。尤其是想更多的了解“事件委托”的时候。

  这次干脆一劳永逸,自己把查出来的资料整理成一篇日志,总结这块的知识,也方便需要的朋友查阅。

JavaScript中事件传播过程那些事儿

  早期的web开发,浏览器厂商很难回答一个哲学上的问题:当你在页面上的一个区域点击时,你真正感兴趣的是哪个元素。这个问题带来了交互的定义。在一个元素的界限内点击,显得有点含糊。毕竟,在一个元素上的点击同时也发生在另一个元素的界限内。例如单击一个按钮。你实际上点击了按钮区域、body元素的区域以及html元素的区域。

  伴随着这个问题,两种主流的浏览器Netscape和IE有不同的解决方案。Netscape定义了一种叫做事件捕获的处理方法,事件首先发生在DOM树的最高层对象(document)然后往最深层的元素传播。在图例中,事件捕获首先发生在document上,然后是html元素,body元素,最后是button元素。

  IE的处理方法正好相反。他们定义了一种叫事件冒泡的方法。事件冒泡认为事件促发的最深层元素首先接收事件。然后是它的父元素,依次向上,知道document对象最终接收到事件。尽管相对于html元素来说,document没有独立的视觉表现,他仍然是html元素的父元素并且事件能冒泡到document元素。所以图例中噢噢那个button元素先接收事件,然后是body、html最后是document。如下图:

图1-JavaScript时间冒泡和捕捉
事件冒泡:

  什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层。这时,从最底层冒出了一个气泡,气泡会一层一层地上升,直到最顶层。而你不管在水的哪一层观察都可以看到并捕捉到这个气泡。好了,把“水”改成“DOM”,把“气泡”改成“事件”。这就是“事件冒泡”。

  气泡带上了某种信息,会告诉其经过的每一层自己是在哪一层产生的。JavaScript的事件确实会带着这个属性。当程序捕获一个事件的时候,它会知道这个事件来自于页面上哪个元素。 事件委托也就是利用这个原理。

事件委托和事件绑定的简介

  事件绑定的介绍,网上比比皆是,大家也比较熟悉,D瓜哥文笔不好,就不献丑了。

  下面我们来重点介绍一下“事件委托”。其实,“事件委托”的概念很简单,生活中也不乏这样的例子。比如,有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

事件委托和事件绑定的代码实现

  事件绑定的代码,网上一抓一把。这里只给出一个简单代码。诸如:

1 document.getElementById("id").onclick=function(){
2    //这里是事件处理代码
3 }

  D瓜哥在第一次注意到事件委托的时候,明白这个大概意思。不过,陆游他老人家有句话说的好,“纸上得来终觉浅,绝知此事要躬行。”英语有一个很“心领神会”的一句话,是世界著名计算机专家Donald Knuth说的,”An algorithm must be seen to be believe!“D瓜哥从第一次注意到“事件委托”后,就考虑用代码如何实现。相信,这也是很多人第一次注意到“事件委托”的想法吧。哈哈

  这里给大家一个简要的示例。示例如下:

01 //document.onclick,从这点就能看出,这个示例把事件委托放到了document上。
02 document.onclick = function(event){
03     //IE doesn't pass in the event object
04     event = event || window.event;
05      
06     //IE uses srcElement as the target
07     var target = event.target || event.srcElement;
08      
09     switch(target.id){
10         case "help-btn":
11                 openHelp();
12                 break;
13         case "save-btn":
14                 saveDocument();
15                 break;
16         case "undo-btn":
17                 undoChanges();
18                 break;
19         //如果有其元素需要处理点击事件,
20         //只需要在这里添加不同的case分支就行。
21     }
22 };

  当然,这里代码为了说明”事件委托”,代码尽可能简化了。不能用于工业生产。另外,为了写例子的方便和内容需要,一下的代码使用jQuery框架来实现相关代码。

  我们了解过了如何使用代码来实现“事件委托”和“事件绑定”了。下面我们从“处理速度”,“新增元素的处理”和“内存消耗”三方面来论述一下这两者的区分。

事件委托和事件绑定的处理速度对比

  先上段代码,让大家体验一下两种事件处理在速度上的巨大差别。业务逻辑很简单:点击按钮,先生成对应数量的元素,然后再分别利用“事件绑定”和“事件委托”各个生成的元素增加事件处理。

  通过上面的测试,可以看出,在元素数量比较大的时候,“事件委托”的效率灰常明显的比“事件绑定”好好的太多太多了(随着元素数量的增加,两者对比实在不成比例,用数量级差别以不足以形容了。哈哈)。在“高性能JavaScript”中,我提出来一些提高JavaSctipt性能的准则。“尽量使用‘事件委托’进行事件处理”也应该做为一条高性能JavaScript准则,加入进去。

事件委托和事件绑定针对新增元素的事件处理的对比

  在一些页面中,难免有时会增加一些新的“元素”。针对这些新增加的元素,事件绑定使如何处理的呢?事件委托又会如何呢?下面,我们还是使用实例来说话。代码如下:

  大家可以点击“点击新增元素”,来新增元素,然后再点击新增出来的元素,看看效果如何?

  大家也看到了,对于“事件绑定”中的新元素,并没有添加事件处理;但是,对于“事件委托”中的新元素,每个新元素都有事件处理。这是为啥相信大家都很明白?这是因为“事件绑定”是针对每个元素进行事件绑定处理,但是新的元素并没有进行事件绑定处理;而“事件委托”,是针对某个选择器下的某种元素,都进行事件处理。所以,只需要这些元素符合这个条件,都会进行事件处理。

事件委托和事件绑定的占用内存对比

  如果一个整体页面里有大量的需要绑定事件的元素,每个元素都要绑定一个函数,而每个函数都是对象,对象就会占用很多内存,内存中的对象越多,性能就越差。

  而事件委托,只在绑定事件的上级元素上绑定函数,次数十分有限(一般每次委托只有一次);另外,在事件被触发时,才会取出触发事件的元素来进一步处理。总体来讲,非常节约内存。

  我们来用实际例子来对比一下。还是利用上面速度测试中的例子,URL如下:
事件绑内存消耗测试URL:http://jsfiddle.net/DiGuaGe/MbYy6/2/embedded/result/
事件委托内存消耗测试URL:http://jsfiddle.net/DiGuaGe/6d7J6/1/embedded/result/
创建元素内存消耗测试URL:http://jsfiddle.net/DiGuaGe/UCXbM/embedded/result/
用IE浏览器打开,然后再打开“Windows任务管理器”,选中“进程”Tab页,注意观察“iexplore.exe”(这个就是IE浏览器的进程),可以看出内存消耗。

  在我的笔记本上(硬件,Dell E6410;Core i5;2G内存;软件:IE8)测试结果差距灰常大。具体对比如下:

图2-JavaScript事件委托和事件绑定的内存消耗对比”

  D瓜哥从“处理速度”、“新增元素事件处理”和“内存消耗”三方面比较了“事件委托”和“事件绑定”的对比,可以很容易看出,“事件委托”在“处理速度”和“内存消耗”上,有得天独厚的优势。所以,在Web编程的时候,尤其在构建大型系统的时候,应该尽量考虑使用“事件委托”。但是,“事件委托”并不是万能的;它也有一些弊端。下面我们在论述一下它的弊端。

使用“事件委托”时,需要注意的问题

  使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。

  这点也可以从jQuery .live()方法的数次升级中看出一些“端倪”。

  jQuery 1.3新增了.live()方法来进行事件处理。刚开始时 .live()方法会把click事件绑定到$(document)对象。代码如下:

1 $("#info_table
td"
).live("click",function(){/*事件处理*/});

  默认把事件绑定到$(document)元素,如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。

  为了避免事件冒泡造成的性能损失,jQuery从1.4开始支持在使用.live()方法时配合使用一个上下文参数。代码如下:

1 $("td",$("#info_table")[0]).live("click",function(){/*事件处理*/});

  这样,”受托方”就从默认的$(document)变成了$(“#infotable”)[0],节省了冒泡的旅程。不过,与.live()共同使用的上下文参数必须是一个单独的DOM元素,所以这里指定上下文对象时使用的是$(“#infotable”)[0],即使用数组的索引操作符来取得的一个DOM元素。

  再说说一点。为了解决无谓生成元素集合的问题,jQuery 1.4.2干脆直接引入了一个新方法.delegate()。使用.delegate(),前面的例子可以这样写:

1 $("#info_table
td"
).live("click",function(){/*事件处理*/});

  《重构与模式》中有一句非常经典的话:“如果想成为一名更优秀的软件设计师,了解优秀软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中隐藏着大智慧。”看来jQuery的live方法的演变,感觉用这句话说这个演变过程是如此的贴切。所以,我们在学习中,也许从一门技术的演变中,得到灵感,创造出更好的技术!

  这篇文章,参考了很多资料。这里表示感谢。另外,大家如果有什么好的见解,欢迎留言进行讨论。

注:

  关于更多的jQuery的介绍,请阅读参考资料中《jQuery代码优化:事件委托篇》。

参考资料:

  1. jQuery代码优化:事件委托篇
  2. JavaScript中的事件委托
  3. JavaScript事件冒泡和事件委托
  4. javascript 事件委托
  5. JavaScript事件委托优化
  6. JavaScript跨浏览器的添加删除事件绑定函数
作 者: D瓜哥,http://www.diguage.com/
原文链接:http://www.diguage.com/archives/71.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

js实例分析JavaScript中的事件委托和事件绑定的更多相关文章

  1. js中的事件委托或是事件代理

    JavaScript(jQuery)中的事件委托 https://www.cnblogs.com/zhoushengxiu/p/5703095.html js中的事件委托或是事件代理详解 https: ...

  2. js中的事件委托和事件代理详解

    起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...

  3. js中的事件委托(事件代理)详解

    本文转载:https://www.cnblogs.com/liugang-vip/p/5616484.html#!comments js中的事件冒泡.事件委托是js 中一些需要注意的小知识点,这里结合 ...

  4. javascript的事件冒泡,阻止事件冒泡和事件委托, 事件委托是事件冒泡的一个应用。

    首先,弄明白js 当中,什么是事件,事件模型在js中是如何设计的.什么是事件冒泡? 什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层.这时,从最底层冒出了一个气泡,气泡会 ...

  5. JS事件委托或者事件代理原理以及实现

    事件委托(事件代理)原理:简单的说就是将事件交由别人来执行,就是将子元素的事件通过冒泡的形式交由父元素来执行. 为什么要用时间委托? 在JavaScript中,添加到页面上的事件处理程序数量将直接关系 ...

  6. javascript中的常用表单事件用法

    下面介绍几种javascript中常用的表单事件: 一,onsubmit:表单中的确认按钮被点击时发生的事件,如下案例. 案例解析:弹出表单中提交的内容 <form name="tes ...

  7. 请写出JavaScript中常用的三种事件。

    请写出JavaScript中常用的三种事件. 解答: onclick,onblur,onChange

  8. PDF.Js的使用—javascript中前端显示pdf文件

    PDF.Js的使用—javascript中前端显示pdf文件 写于2018/12/6 起因是一个图片展示页面需要展示pdf格式的文件,所以查了半天决定使用pdf.js,我也不求有多了解它,能实现我想要 ...

  9. 原生js事件委托(事件代理)方法扩展

    原生js事件委托(事件代理)方法扩展: 通过Node底层原型扩展委托方法 /** * 事件委托方法 * @param eventName {string}:事件名称,如'click' * @param ...

随机推荐

  1. java.security.NoSuchAlgorithmException: AES KeyGenerator not available

    异常信息 Caused by: Java.lang.IllegalStateException: Unable to acquire AES algorithm. This is required t ...

  2. HttpServletRequestWrapper

    1). why 需要改变从 Servlet 容器 (可能是任何的 Servlet 容器)中传入的 HttpServletRequest 对象的某个行为,该怎么办? 一. 继承 HttpServletR ...

  3. Spring源码阅读(七)

    这一讲主要分析bean注册过程中各种初始化方法回调的执行逻辑(initializeBean) /** * Initialize the given bean instance, applying fa ...

  4. Linux基础命令---netstat显示网络状态

    netstat netstat指令可以显示当前的网络连接.路由表.接口统计信息.伪装连接和多播成员资格等信息. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.open ...

  5. Linux基础命令---apwatch

    arpwatch      arpwatch指令可以监听网络设备和ip地址的对应关系,将发现的信息发送到系统日志“/var/log/message”. 此命令的适用范围:RedHat.RHEL.Ubu ...

  6. C# 设置按钮快捷键

    参考自:http://www.csharpwin.com/csharpspace/3932r8132.shtml 一.C# button快捷键之第一种:Alt + *(按钮快捷键) 在Button按钮 ...

  7. django 集合

    1,前言 socket 位于应用层和传输层之间的一个抽象层,它是一个接口. 百度的服务器(socket服务端) . 启动socket . 绑定ip和端口 . 监听 . 接收数据 . 发送数据 . 断开 ...

  8. Codeforce 835B - The number on the board (贪心)

    Some natural number was written on the board. Its sum of digits was not less than k. But you were di ...

  9. 关于reduce输出write方法

    关于hadoop一些自定义输出 code>OutputFormat</code> describes the output-specification for a * Map-Red ...

  10. OGG 12.3中支持系统procedure复制的几点说明

    如果需要同步系统级别的过程和package,则需要满足以下条件: 要求使用OGG12.3及以后的版本 需要使用oracle db12.2及以后的版本 需要使用集成抽取和集成投递 在DBA_GG_SUP ...