jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on
事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应
事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行
为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分
网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新总结一下
jQuery对事件的绑定分别有几个API http://www.css88.com/jqapi-1.9/
.bind()
.live()
.delegate()
.on()
不管是用什么方式绑定,归根到底还是用addEventListener/attachEvent处理的,正如选择器一样不管如何匹配最终还是那么几个浏览器接口处理
既然如此,事件为什么还要区分那么多不同的处理方案?
这里就要涉及到DOM事件处理模型了,就是常提到的捕获与冒泡
传统的事件处理:
给某一元素绑定了一个点击事件,传入一个回调句柄处理
element.addEventListener('click',doSomething,false)
这样的代码再正常不过了
但是,如果页面上有几个百元素需要绑定(假设),那么务必就要绑定几百次啦.
这样问题就出现了:
第一:大量的事件绑定,性能消耗,而且还需要解绑(IE会泄漏)
第二:绑定的元素必须要存在
第三: 后期生成HTML会没有事件绑定,需要重新绑定
第四: 语法过于繁杂
…………
有没有办法优化呢? 答案是肯定的
事件委托
DOM有个事件流的特性,也就是说我们在页面上触发节点的时候事件都会上下或者向上传播,事件捕捉和事件冒泡。
借个图:
DOM2.0模型将事件处理流程分为三个阶段:一、事件捕获阶段,二、事件目标阶段,三、事件起泡阶段。
事件传送可以分为3个阶段。
(1).在事件捕捉(Capturing)阶段,事件将沿着DOM树向下转送,目标节点的每一个祖先节点,直至目标节点。例如,若用户单击了一个超链接,则该单击事件将从document节点转送到html元素,body元素以及包含该链接的p元素。在此过程中,浏览器都会检测针对该事件的捕捉事件监听器,并且运行这件事件监听器。
(2)在目标(target)阶段,浏览器在查找到已经指定给目标事件的事件监听器之后,就会运行 该事件监听器。目标节点就是触发事件的DOM节点。例如,如果用户单击一个超链接,那么该链接就是目标节点(此时的目标节点实际上是超链接内的文本节点)。
(3).在冒泡(Bubbling)阶段,事件将沿着DOM树向上转送,再次逐个访问目标元素的祖先节点到document节点。该过程中的每一步。浏览器都将检测那些不是捕捉事件监听器的事件监听器,并执行它们。
利用事件传播(这里是冒泡)这个机制,就可以实现事件委托。
具体来说,事件委托就是事件目标自身不处理事件,而是把处理任务委托给其父元素或者祖先元素,甚至根元素(document)
jQuery的事件优化
这么好的特性jQuery当然不会放过,所以就衍生出 .bind()、.live() .on()和.delegate()
jQuery的事件绑定有多个方法可以调用,以click事件来举例:
- click方法
- bind方法
- delegate方法
- on方法
这里要清楚的认识:不管你用的是(click / bind / delegate)之中那个方法,最终都是jQuery底层都是调用on方法来完成最终的事件绑定。
因此从某种角度来讲除了在书写的方便程度及习惯上挑选,不如直接都采用on方法来的痛快和直接
所以在新版的API中都这么写到:
.on()
方法事件处理程序到当前选定的jQuery对象中的元素。在jQuery 1.7中,.on()
方法 提供绑定事件处理的所有功能
性能对比
我们来个对直观的测试
生成999个DOM节点,不做任何处理,内存消耗2.2M
给每一个元素绑定click事件,增加到5.6M
$('.ul a').click(function(e){
alert('click event');
});
委托事件2.2M
$('.ul').on('click', 'a', function(e){
alert('click event');
});
效果不言而喻了,除了性能的差异,通过委托的事件还能很友好的支持动态绑定
只要on的delegate对象是HTML页面原有的元素,由于是事件的触发是通过Javascript的事件冒泡机制来监测,所以对于所有子元素(包括后期通过JS生成的元素)所有的事件监测均能有效,且由于不用对多个元素进行事件绑定,能够有效的节省内存的损耗。
.bind()
.bind()
方法用于直接附加一个事件处理程序到元素上。
处理程序附加到jQuery对象中当前选中的元素,所以,在.bind()
绑定事件的时候,这些元素必须已经存在
很明显就是直接调用的,没利用委托机制
.live()
将委托的事件处理程序附加到一个页面的document
元素,从而简化了在页面上动态添加的内容上事件处理的使用。
例如:
$('a').live('click', function() { alert("!!!") });
JQuery把alert函数绑定到$(document)元素上,并使用’click’和’a’作为参数。
任何时候只要有事件冒泡到document节点上,它就查看该事件是否是一个click事件,以及该事件的目标元素与’a’这一CSS选择器是否匹配,如果都是的话,则执行函数。
因为更高版本的jQuery提供了更好的方法,没有.live()
方法的缺点,所以.live()
方法不再推荐使用
特别是,使用.live()
出现的以下问题:
- 在调用
.live()
方法之前,jQuery 会先获取与指定的选择器匹配的元素,这一点对于大型文档来说是很花费时间的。 - 不支持链式写法。例如,
$("a").find(".offsite, .external").live( ... );
这样的写法是不合法的,并不能像期待的那样起作用。 - 由于所有的
.live()
事件被添加到document
元素上,所以在事件被处理之前,可能会通过最长最慢的那条路径之后才能被触发。 - 在移动 iOS (iPhone, iPad 和 iPod Touch) 上,对于大多数元素而言,
click
事件不会冒泡到文档 body 上,并且如果不满足如下情况之一,就不能和.live()
方法一起使用:- 使用原生的可被点击的元素,例如,
a
或button
,因为这两个元素可以冒泡到document
。 - 在
document.body
内的元素使用.on()
或.delegate()
进行绑定,因为移动 iOS 只有在 body 内才能进行冒泡。 - 需要 click 冒泡到元素上才能应用的 CSS 样式
cursor:pointer
(或者是父元素包含document.documentElement
)。但是依需注意的是,这样会禁止元素上的复制/粘贴功能,并且当点击元素时,会导致该元素被高亮显示。
- 使用原生的可被点击的元素,例如,
- 在事件处理中调用
event.stopPropagation()
来阻止事件处理被添加到 document 之后的节点中,是效率很低的。因为事件已经被传播到document
上。 .live()
方法与其它事件方法的相互影响是会令人感到惊讶的。例如,$(document).unbind("click")
会移除所有通过.live()
添加的 click 事件!
.delegate()
为了突破单一.bind()方法的局限性,实现事件委托,jQuery 1.3引入了.live()方法。后来,为解决“事件传播链”过长的问题,jQuery 1.4又支持为.live()方法指定上下文对象。而为了解决无谓生成元素集合的问题,jQuery 1.4.2干脆直接引入了一个新方法.delegate()。
使用.delegate(),前面的例子可以这样写:
$('#element).delegate('a', 'click', function() { alert("!!!") });
JQuery扫描文档查找$(‘#element’),并使用click事件和’a’这一CSS选择器作为参数把alert函数绑定到$(‘#element)上。
任何时候只要有事件冒泡到$(‘#element)上,它就查看该事件是否是click事件,以及该事件的目标元素是否与CCS选择器相匹配。如果两种检查的结果都为真的话,它就执行函数。
可以注意到,这一过程与.live()类似,但是其把处理程序绑定到具体的元素而非document这一根上
那么 $('a').live() == $(document).delegate('a') ?
可见,.delegate()方法是一个相对完美的解决方案。但在DOM结构简单的情况下,也可以使用.live()。
.on()
其实.bind(), .live(), .delegate()都是通过.on()来实现的,.unbind(), .die(), .undelegate(),也是一样的都是通过.off()来实现的
提供了一种统一绑定事件的方法
总结:
在下列情况下,应该使用.live()或.delegate(),而不能使用.bind():
- 为DOM中的很多元素绑定相同事件;
- 为DOM中尚不存在的元素绑定事件;
用.bind()的代价是非常大的,它会把相同的一个事件处理程序hook到所有匹配的DOM元素上
不要再用.live()了,它已经不再被推荐了,而且还有许多问题
.delegate()会提供很好的方法来提高效率,同时我们可以添加一事件处理方法到动态添加的元素上
我们可以用.on()来代替上述的3种方法
不足点也是有的:
- 并非所有的事件都能冒泡,如load, change, submit, focus, blur
- 加大管理复杂。
- 不好模拟用户触发事件
如何取舍就看项目实际中运用了。
jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on的更多相关文章
- jQuery 2.0.3 源码分析 事件体系结构
那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- Spark2.1.0之源码分析——事件总线
阅读提示:阅读本文前,最好先阅读<Spark2.1.0之源码分析——事件总线>.<Spark2.1.0事件总线分析——ListenerBus的继承体系>及<Spark2. ...
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- jQuery 2.0.3 源码分析 Deferred概念
JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...
- jQuery 2.0.3 源码分析 Deferrred概念
转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...
- jQuery 2.0.3 源码分析core - 选择器
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象 ...
- jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...
随机推荐
- 网站开发网页广告条不显示,出现ERR_BLOCKED_BY_CLIENT
原因: 原因很简单,是因为被360浏览器的ad block给屏蔽掉了. 现象: 解决方案: 更换图片文件名,去掉ad,改成其他的字符.
- phpcms文章点击量统计方法
phpcms用户广大,很好用,很傻瓜.设计思路也很好,对cms的常见功能都有设计,可以作为自己开发的参考. 最近看了下phpcms的源码关于文章点击量统计的这块,自己记录下. 默认文章点击量显示的位置 ...
- C#调用C++动态库方法及动态库封装总结
我只是粗浅的学习过一些C++语法, 变量类型等基础内容, 如有不对的地方还望指出. 如果你跟我一样, 对指针操作不了解, 对封装C++动态库头疼的话, 下面内容还是有帮助的. 转载请注明出处: htt ...
- C语言-占位符
%a 浮点数.十六进制数字和p-记数法(C99) %A 浮点数.十六进制数字和p-记法(C99) %c 一个字符 %d 有符号十进制整数 %e 浮点数.e-记数法 %E ...
- 【BZOJ】4002: [JLOI2015]有意义的字符串
题意 求$\left \lfloor \left( \frac{b+\sqrt{d}}{2} \right)^n \right \rfloor \pmod {7528443412579576937} ...
- Day 2:增加SplashScreen
If you want to add just single image, then create a pic in the size of 480*800 and name it as Splash ...
- PNG-8和PNG-24的抉择
今天我做了一个图,因为需要透明,所以我存为了PNG8格式,结果发现图片变了,图片变得四周都不光滑了,四周都变得有锯齿了,而且阴影也不见了,后来存为PNG24,这些问题就消失了.我去百度搜索了关于PNG ...
- BZOJ4596: [Shoi2016]黑暗前的幻想乡
Description 四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖 怪涌入了幻想乡,扰乱了幻想乡昔日的秩序.但是幻想乡的建制派妖怪(人类) 博丽灵梦和八云紫等人整日高谈所有妖怪 ...
- mysql问题总结
1. You are using safe update mode and you tried to update a table without a WHERE clause that uses a ...
- linux上安装activeMQ
1.新建一个文件夹activeMQ mkdir /server 2.授权 chmod 777 /server 3.下载activeMQ安装包,拷贝到/activeMQ目录下apache-acti ...