// 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上的该类型的事件处理对象移除