微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

jQuery 1.9.1源码分析系列十事件系统之绑定事件

事件绑定的方法有很多种,使用了jquery那么原理那种绑定方式(elem.click = function(){...}))就不太想推荐给大家了。最主要的原因是elem.click=fn这种方式只能绑定一个事件处理,多次绑定的只会保留最后一次绑定的结果。

下面给大家介绍jquery绑定事件的方式有哪些吧。

代码如下:

比如eventType指的是事件类型,比如click: $("#chua").click(fn);

data这个参数一般都不会使用。这种方式事件绑定在("#chua")上,没有委托事件,和js原生的事件绑定更接近。我们看一下源码

0 ?       this.on( name,null,data,fn ) :       //如果不带参数表示立刻触发指定事件       this.trigger( name );   }; }); jQuery.fn.bind( types[,data],fn )

  比如$("#chua").bind("click",fn)。直接将事件绑定到$("#chua")上,没有委托事件。源码

rush:js;"> bind: function( types,fn ) { return this.on( types,fn ); },unbind: function( types,fn ) { return this.off( types,fn ); } jQuery.fn.delegate(selector,types[,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源码

rush:js;"> delegate: function( selector,types,selector,undelegate: function( selector,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处理。源码

rush:js;"> one: function( types,fn ) { return this.on( types,fn,1 ); } jQuery.fn.trigger(type[,data]) jQuery.fn.triggerHandler(type[,data])

  trigger触发jQuery对象所匹配的每一个元素对应type类型的事件。比如$("#chua").trigger("click");

  triggeHandler只触发jQuery对象所匹配的元素中的第一个元素对应的type类型的事件,且不会触发事件的认行为。

rush:js;"> //立刻触发jQuery对象内所有元素的指定type的事件 trigger: function( type,data ) { return this.each(function() { jQuery.event.trigger( type,this ); }); },//立刻触发jQuery对象内第一个元素的指定type的事件,且不会触发事件(比如表单提交)的认行为 triggerHandler: function( type,data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type,elem,true ); } }

  上面分析了那么些个事件绑定,有么有发现他们都是使用.on方式绑定的?这也是为什么提倡统一使用on来绑定的原因(one方式除外)。

rush:js;"> jQuery.fn.on( types[,fn )

  .on的事件绑定一半的代码都实在处理传递不同参数的处理,这也是jQuery的口号Write less,do more的代价吧。最终使用jQuery.event.add来绑定事件。

 

 jQuery.event.add绑定事件有几个比较关键的地方:

  第一个,使用内部缓存来保存节点elem的事件信息

rush:js;"> //获取缓存数据        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

+~]|:(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]对应的事件不是委托事件,而是节点自身的事件。

rush:js;"> //将事件对象handleObj添加到元素的处理列表,委托事件放在前面,委托代理计数递增 if ( selector ) { handlers.splice( handlers.delegateCount++,handleObj ); } else { handlers.push( handleObj ); }

  源码和添加事件后的结构上一章已经分析,

  绑定有一个公用函数jQuery.fn.on。解绑同样有一个公用函数jQuery.fn.off

rush:js;"> jQuery.fn.off([ types[,selector][,fn]] )

  这里的传参有个比较特殊的情况:当types是浏览器事件对象event的时候,表示要去掉(解绑)委托节点上event.selector指定的委托事件

rush:js;"> //传入的参数是事件且绑定了处理函数 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完整的源码如下

rush:js;"> off: function( types,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.handler ); return this;   }   if ( typeof types === "object" ) { // ( types-object [,selector] ) for ( type in types ) { this.off( type,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,selector );   }); }

  

接下来分析一下事件解绑的低级api jQuery.event.remove。

jQuery.event.remove

  jQuery使用.off()函数伤处绑定的事件时内部调用的基础函数是jQuery.event.remove。该函数的处理流程如下

  1. 分解传入的要删除的事件类型types,遍历类型,如果要删除的事件没有事件名,只有命名空间则表示删除该命名空间下所有绑定事件

rush:js;"> //分解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,true );
}
continue;
}
...

  2. 遍历类型过程中,删除匹配的事件,代理计数修正

rush:js;"> 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上的该类型的事件处理对象移除

rush:js;"> // 移除事件处理对象 // (移除特殊事件处理过程中避免潜在的无限递归,下一章会专门详解这种情况) 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

rush:js;"> if ( jQuery.isEmptyObject( events ) ) {   delete elemData.handle;   //removeData还检事件对象是否为空,所以使用它替代delete   jQuery._removeData( elem,"events" ); }

拓展: 浏览器事件删除jQuery.removeEvent

rush:js;"> jQuery.removeEvent = document.removeEventListener ? function( elem,handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type,handle,false ); } } : function( elem,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源码分析系列(十)事件系统之绑定事件,希望大家喜欢。

原文地址:https://www.jb51.cc/jquery/51625.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐