【JQuery源码】事件绑定
事件绑定的方式有很多种。使用了jQuery那么原来那种绑定方式(elem.click = function(){...})就不推荐了,原因?
最主要的一个原因是elem.click = fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果。
看一下jQuery绑定事件的方式有哪些
jQuery.fn.eventType([[data,] fn])
比如eventType指的是事件类型,比如click: $("#chua").click(fn);
data这个参数一般都不会使用。这种方式事件绑定在("#chua")上,没有委托事件,和js原生的事件绑定更接近。我们看一下源码
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
//合并15种事件统一增加到jQuery.fn上,内部调用this.on / this.trigger
jQuery.fn[ name ] = function( data, fn ) {
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
//如果不带参数表示立刻触发指定事件
this.trigger( name );
};
});
jQuery.fn.bind( types[, data], fn )
比如$("#chua").bind("click",fn)。直接将事件绑定到$("#chua")上,没有委托事件。源码
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
}
jQuery.fn.delegate(selector, types[, data], fn)
顾名思义delegate这个函数是用来做事件委托的,将选择器selector对应的响应处理委托给当前jQuery所匹配的元素。
比如:$(document).delegate('#big',"click",dohander);分析到这里顺便分析一下事件委托的处理流程。
当点击"#big"元素的时候,事件click会冒泡直到document节点;
document绑定了处理事件,这个处理事件会调用到事件分发器dispatch;
dispatch中取出对应事件类型click的所有的委托事件列表handlers;
根据事件源event.target过滤出委托事件列表handlers中每一个元素的selector属性对应的节点处于事件原和委托节点document之间(包括事件源)的委托事件,保存为handlerQueue;
执行handlerQueue里面的事件处理。
上面是一个大致的流程,后续会详细分析。先看delegate源码
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
// ( namespace ) or ( selector, types [, fn] )
return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
}
jQuery.fn.one( types[, selector[, data]], fn )
通过one()
函数绑定的事件处理函数都是一次性的,只有首次触发事件时会执行该事件处理函数。触发之后,jQuery就会移除当前事件绑定。
比如$("#chua").one("click",fn);为#chua节点绑定一次性的click事件
$(document).one("click","#chua",fn);将#chua的click事件委托给document处理。源码
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );
}
jQuery.fn.trigger(type[, data])
jQuery.fn.triggerHandler(type[, data])
trigger触发jQuery对象所匹配的每一个元素对应type类型的事件。比如$("#chua").trigger("click");
triggeHandler只触发jQuery对象所匹配的元素中的第一个元素对应的type类型的事件,且不会触发事件的默认行为。
//立刻触发jQuery对象内所有元素的指定type的事件
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
//立刻触发jQuery对象内第一个元素的指定type的事件,且不会触发事件(比如表单提交)的默认行为
triggerHandler: function( type, data ) {
var elem = this[0];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
上面分析了那么些个事件绑定,有么有发现他们都是使用.on方式绑定的?这也是为什么提倡统一使用on来绑定的原因(one方式除外)。
jQuery.fn.on( types[, selector[, data]], fn )
.on的事件绑定一半的代码都实在处理传递不同参数的处理,这也是jQuery的口号Write less, do more的代价吧。最终使用jQuery.event.add来绑定事件。
jQuery.event.add绑定事件有几个比较关键的地方:
第一个,使用内部缓存来保存节点elem的事件信息
//获取缓存数据
elemData = jQuery._data( elem );
...
//设置缓存数据
if ( !(events = elemData.events) ) {
events = elemData.events = {};
}
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
...
};
//将elem作为handle函数的一个特征防止ie非本地事件引起的内存泄露
eventHandle.elem = elem;
}
第二个,设置绑定事件信息,特别是指定的选择器selector、响应处理handler、响应事件类型type、命名空间namespace
// handleObj:设置绑定事件信息。贯穿整个事件处理
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
// For use in libraries implementing .is(). We use this for POS matching in `select`
//"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
//whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
//用来判断亲密关系
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn );
第三个,节点的事件列表中,真正的委托事件列表放置在前面,和delegateCount属性同步,即events.click.length假设为3,events.click.delegateCount假设为2。那么events.click[0]和events.click[1]所指定事件是委托事件。第三个events.click[2]对应的事件不是委托事件,而是节点自身的事件。
//将事件对象handleObj添加到元素的处理列表,委托事件放在前面,委托代理计数递增
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
源码和添加事件后的结构上一章已经分析,详情请点击查看
绑定有一个公用函数jQuery.fn.on。解绑同样有一个公用函数jQuery.fn.off
jQuery.fn.off([ types[, selector][, fn]] )
这里的传参有个比较特殊的情况:当types是浏览器事件对象event的时候,表示要去掉(解绑)委托节点上event.selector指定的委托事件
//传入的参数是事件且绑定了处理函数
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
handleObj = types.handleObj;
//types.delegateTarget是事件托管对象
jQuery( types.delegateTarget ).off(
//组合jQuery识别的type
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
无论如何最终都是调用jQuery.event.remove函数来解绑事件。
jQuery.fn.off完整的源码如下
接下来分析一下事件解绑的低级api jQuery.event.remove。
jQuery.event.remove
jQuery使用.off()函数伤处绑定的事件时内部调用的基础函数是jQuery.event.remove。该函数的处理流程如下
1. 分解传入的要删除的事件类型types,遍历类型,如果要删除的事件没有事件名,只有命名空间则表示删除该命名空间下所有绑定事件
//分解types为type.namespace为单位元素的数组
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {
tmp = rtypenamespace.exec( types[t] ) || [];
type = origType = tmp[1];
namespaces = ( tmp[2] || "" ).split( "." ).sort(); //解绑当前元素的当前命名空间(types[ t ])上所有的事件
if ( !type ) {
for ( type in events ) {
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
}
continue;
}
...
2. 遍历类型过程中,删除匹配的事件,代理计数修正
type = ( selector ? special.delegateType : special.bindType ) || type;
handlers = events[ type ] || [];
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); //删除匹配事件
origCount = j = handlers.length;
while ( j-- ) {
handleObj = handlers[ j ]; //各种满足移除事件的条件才能移除
if ( ( mappedTypes || origType === handleObj.origType ) &&
( !handler || handler.guid === handleObj.guid ) &&
( !tmp || tmp.test( handleObj.namespace ) ) &&
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
handlers.splice( j, 1 );
if ( handleObj.selector ) {
handlers.delegateCount--;
}
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
}
3. 如果节点上指定类型的事件处理器已经为空,则将events上的该类型的事件处理对象移除
// 移除事件处理对象
// (移除特殊事件处理过程中避免潜在的无限递归,下一章会专门详解这种情况)
if ( origCount && !handlers.length ) {
//例如 var js_obj = document.createElement("div"); js_obj.onclick = function(){ …}
//上面的js_obj是一个DOM元素的引用,DOM元素它长期在网页当中,不会消失,而这个DOM元素的一属性onclick,又是内部的函数引用(闭包),而这个匿名函数又和js_obj之间有隐藏的关联(作用域链)所以形成了一个,循环引用.
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
} delete events[ type ];
}
4. 如果节点上没有任何绑定的事件,则清空事件处理入口handle
if ( jQuery.isEmptyObject( events ) ) {
delete elemData.handle;
//removeData还检事件对象是否为空,所以使用它替代delete
jQuery._removeData( elem, "events" );
}
拓展: 浏览器事件删除jQuery.removeEvent
jQuery.removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
var name = "on" + type;
if ( elem.detachEvent ) {
// #8545, #7054,避免自定义事件在IE6-8中的内存泄露
// detachEvent需要传递第一个参数,不能是undefined的
if ( typeof elem[ name ] === core_strundefined ) {
elem[ name ] = null;
}
elem.detachEvent( name, handle );
}
};
【JQuery源码】事件绑定的更多相关文章
- jQuery源码解读-事件分析
最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventList ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- jQuery源码:从原理到实战
jQuery源码:从原理到实战 jQuery选择器对象 $(".my-class"); document.querySelectorAll*".my-class" ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jQuery中是事件绑定方式--on、bind、live、delegate
概述:jQuery是我们最常用的js库,对于事件的绑定也是有很多种,on.one.live.bind.delegate等等,接下来我们逐一来进行讲解. 本片文章中事件所带的为版本号,例:v1.7+为1 ...
- jQuery源码浅析2–奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- jQuery源码分析系列(转载来源Aaron.)
声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...
- jquery 源码学习(四)构造jQuery对象-工具函数
jQuery源码分析-03构造jQuery对象-工具函数,需要的朋友可以参考下. 作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 声明:本文为原 ...
- jQuery 源码分析(十六) 事件系统模块 底层方法 详解
jQuery事件系统并没有将事件监听函数直接绑定到DOM元素上,而是基于数据缓存模块来管理监听函数的,事件模块代码有点多,我把它分为了三个部分:分底层方法.实例方法和便捷方法.ready事件来讲,好理 ...
- jQuery源码分析系列——来自Aaron
jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...
随机推荐
- webservice大文件怎么传输
版权所有 2009-2018荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...
- SecureCRT和乱码
示例: # ls /usr/local/r3c/bin/lib /bin/ls: /usr/local/r3c/bin/lib: ????????? 查看系统字符集设置: # locale LANG= ...
- Java内存模型(二)
volatile型变量的特殊规则 volatile是Java虚拟机提供的最轻量级的同步机制,当一个变量被定义成volatile后,它将具备两种特性,第一是保证此变量对所有线程的可见性,这里的“可见性” ...
- Socket常用语法与socketserver实例
1>Socket相关: 1>Socket Families(地址簇): socket.AF_UNIX 本机进程间通信 socket.AF_INET IPV4 socket.AF_I ...
- hibernate 一对一 one to one的两种配置方式
hibernate中one-to-one两种配置方式 标签: hibernateHibernateone-to-one 2013-02-19 17:44 11445人阅读 评论(1) 收藏 举报 分 ...
- [jquery-delegate] iphone_4s _iphone _5c_中不兼容jQuery delegate 事件(does not wok)
1. jQuery .on() and .delegate() doesn't work on iPad http://stackoverflow.com/questions/10165141/jqu ...
- N个不同球取出M个的组合个数求解
Technorati 标签: 组合,概率 从N个不同的球中取出M个,一共有多少种取法? 这个问题是组合数据的基本问题,考虑拿出球是否放回,拿出去的球是否有序,它有4种变体: 不放回,有序: 不放回,无 ...
- NET 读取Word文档信息
1.添加程序集引用:WindowsBase 2.添加nuget:DocumentFormat.OpenXml 3.代码: var wordPath = @"C:\xxx.docx" ...
- HTML5 canvas 学习
一.canvas简介 <canvas> 是 HTML5 新增的,一个可以使用脚本(通常为JavaScript)在其中绘制图像的 HTML 元素.它可以用来制作照片集或者制作简单(也不是 ...
- [Xamarin]我的Xamarin填坑之旅(一)
一想到明天是星期五,不对,是今天,心里就很激动,毕竟明天没课.激动之余,来写一篇博客,记录一下最近踏坑Xamarin开发校园助手APP的一些事儿.也许更像是一篇流水账. 在扯Xamarin之前,有必要 ...