事件绑定的方法有很多种,使用了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.triggerjquery.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完整的源码如下
off: function( types, selector, fn ) {var handleobj, type;//传入的参数是事件且绑定了处理函数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;}if ( typeof types === object ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this;}if ( selector === false || typeof selector === function ) { // ( types [, fn] ) fn = selector; selector = undefined;}if ( fn === false ) { fn = returnfalse;}return this.each(function() { jquery.event.remove( this, types, fn, selector );});}
接下来分析一下事件解绑的低级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还检事件对象是否为空,所以使用它替代deletejquery._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 1.9.1源码分析系列(十)事件系统之绑定事件,希望大家喜欢。