js实例分析JavaScript中的事件委托和事件绑定
我们在学习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。如下图:
事件冒泡:
什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层。这时,从最底层冒出了一个气泡,气泡会一层一层地上升,直到最顶层。而你不管在水的哪一层观察都可以看到并捕捉到这个气泡。好了,把“水”改成“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)测试结果差距灰常大。具体对比如下:
D瓜哥从“处理速度”、“新增元素事件处理”和“内存消耗”三方面比较了“事件委托”和“事件绑定”的对比,可以很容易看出,“事件委托”在“处理速度”和“内存消耗”上,有得天独厚的优势。所以,在Web编程的时候,尤其在构建大型系统的时候,应该尽量考虑使用“事件委托”。但是,“事件委托”并不是万能的;它也有一些弊端。下面我们在论述一下它的弊端。
使用“事件委托”时,需要注意的问题
使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。
这点也可以从jQuery .live()方法的数次升级中看出一些“端倪”。
jQuery 1.3新增了.live()方法来进行事件处理。刚开始时 .live()方法会把click事件绑定到$(document)对象。代码如下:
1 |
$( "#info_table ).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 ).live( "click" , function (){ /*事件处理*/ }); |
《重构与模式》中有一句非常经典的话:“如果想成为一名更优秀的软件设计师,了解优秀软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中隐藏着大智慧。”看来jQuery的live方法的演变,感觉用这句话说这个演变过程是如此的贴切。所以,我们在学习中,也许从一门技术的演变中,得到灵感,创造出更好的技术!
这篇文章,参考了很多资料。这里表示感谢。另外,大家如果有什么好的见解,欢迎留言进行讨论。
注:
关于更多的jQuery的介绍,请阅读参考资料中《jQuery代码优化:事件委托篇》。
参考资料:
- jQuery代码优化:事件委托篇
- JavaScript中的事件委托
- JavaScript事件冒泡和事件委托
- javascript 事件委托
- JavaScript事件委托优化
- JavaScript跨浏览器的添加删除事件绑定函数
原文链接:http://www.diguage.com/archives/71.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
js实例分析JavaScript中的事件委托和事件绑定的更多相关文章
- js中的事件委托或是事件代理
JavaScript(jQuery)中的事件委托 https://www.cnblogs.com/zhoushengxiu/p/5703095.html js中的事件委托或是事件代理详解 https: ...
- js中的事件委托和事件代理详解
起因: 1.这是前端面试的经典题型,要去找工作的小伙伴看看还是有帮助的: 2.其实我一直都没弄明白,写这个一是为了备忘,二是给其他的知其然不知其所以然的小伙伴们以参考: 概述: 那什么叫事件委托呢?它 ...
- js中的事件委托(事件代理)详解
本文转载:https://www.cnblogs.com/liugang-vip/p/5616484.html#!comments js中的事件冒泡.事件委托是js 中一些需要注意的小知识点,这里结合 ...
- javascript的事件冒泡,阻止事件冒泡和事件委托, 事件委托是事件冒泡的一个应用。
首先,弄明白js 当中,什么是事件,事件模型在js中是如何设计的.什么是事件冒泡? 什么是“事件冒泡”呢?假设这里有一杯水,水被用某种神奇的方式分成不同颜色的几层.这时,从最底层冒出了一个气泡,气泡会 ...
- JS事件委托或者事件代理原理以及实现
事件委托(事件代理)原理:简单的说就是将事件交由别人来执行,就是将子元素的事件通过冒泡的形式交由父元素来执行. 为什么要用时间委托? 在JavaScript中,添加到页面上的事件处理程序数量将直接关系 ...
- javascript中的常用表单事件用法
下面介绍几种javascript中常用的表单事件: 一,onsubmit:表单中的确认按钮被点击时发生的事件,如下案例. 案例解析:弹出表单中提交的内容 <form name="tes ...
- 请写出JavaScript中常用的三种事件。
请写出JavaScript中常用的三种事件. 解答: onclick,onblur,onChange
- PDF.Js的使用—javascript中前端显示pdf文件
PDF.Js的使用—javascript中前端显示pdf文件 写于2018/12/6 起因是一个图片展示页面需要展示pdf格式的文件,所以查了半天决定使用pdf.js,我也不求有多了解它,能实现我想要 ...
- 原生js事件委托(事件代理)方法扩展
原生js事件委托(事件代理)方法扩展: 通过Node底层原型扩展委托方法 /** * 事件委托方法 * @param eventName {string}:事件名称,如'click' * @param ...
随机推荐
- weblogic 乱码
1.找到weblogic安装目录,当前项目配置的domain 2.找到bin下的setDomainEnv.cmd文件 3.打开文件,从文件最后搜索第一个set JAVA_OPTIONS=%JAVA_O ...
- unittest和pytest的区别
一.用例编写规则 1.unittest提供了test cases.test suites.test fixtures.test runner相关的类,让测试更加明确.方便.可控.使用unittest编 ...
- 解决keepalived脑裂问题
检测思路:正常情况下keepalived的VIP地址是在主节点上的,如果在从节点发现了VIP,就设置报警信息 脚本如下: #!/bin/bash # 检查脑裂的脚本,在备节点上进行部署 LB01_VI ...
- 原生tab选项卡制作
html部分 <div class="tab"> <div class="nav"> <ul> <li class=& ...
- jetbrain_ia
在激活Jetbrains旗下任意产品的时候选择激活服务器填入以下地址便可成功激活 http://idea.liyang.io 最新方法(2018.3.4) http://blog.csdn.net/w ...
- mysql执行计划查看工具explain
在优化sql语句时,我们经常会用到explain语句,这里对explain语句做一个详细的总结说明. The EXPLAIN statement provides information about ...
- linux下SVN忽略指定文件/文件夹
http://www.cnblogs.com/fjping0606/p/4743009.html 1.配置SVN默认编辑器vi ~/.bash_profile最后一行加上:export SVN_EDI ...
- Golang中map的三种声明方式和简单实现增删改查
package main import ( "fmt" ) func main() { test3 := map[string]string{ "one": & ...
- jQuery实现全选与全不选功能
初始时: 实现功能后: 实现该功能的核心代码: <script> $(function(){ $("#selectBtn").click(function(){ con ...
- keras与tensorboard结合使用
使用tensorboard将keras的训练过程显示出来(动态的.直观的)是一个绝好的主意,特别是在有架设好的VPS的基础上,这篇文章就是一起来实现这个过程. 一.主要原理 keras的在训练(fit ...