事件绑定的方式有很多种。使用了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完整的源码如下
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 ); });}
新闻热点
疑难解答