最近应用开发的过程中出现了一个小问题,顺便记录一下原因和方法--事件触发
前文要主分析了添加事件监听的方法,本文则要主讲删除事件监听,以及事件模拟。
jQuery.fn.off
jQuery.fn.off = function( types, selector, fn ) { var handleObj, type; // 如果types是象对,其实在现应该说是type,并且具有preventDefalut和handleObj if ( types && types.preventDefault && types.handleObj ) { // 通过types取获handleObj handleObj = types.handleObj; // 转成字符串来消取事件 jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } // 如果types还是象对,那么为认其是是一个map,key对应事件名,value对应理处函数 if ( typeof types === "object" ) { // ( types-object [, selector] ) // 遍历部全type for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } // 如果selector为false,或者selector是个函数 if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) // 等同于传进来types和fn fn = selector; selector = undefined; } if ( fn === false ) { // 如果fn是false,则定义为一个return false的函数 fn = returnFalse; } // 遍历部全元素 return this.each(function() { // 应用jQuery.event.remove删除部全事件理处 jQuery.event.remove( this, types, fn, selector ); }); };
这个方法逻辑还是比拟清晰的,试尝理处各种传参式方当前,终最都是用利jQuery.event.remove来删除事件理处函数的。
jQuery.event.remove
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, // 通过部内存缓取获象对关相数据 elemData = jQuery.hasData( elem ) && jQuery._data( elem ); // 如果没有关相存缓数据,或者存缓中没有关相理处表列,则这个象对没事件可删除 if ( !elemData || !(events = elemData.events) ) { // 退出 return; } // types多是通过空格分开的多个type,转成组数 types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; // 遍历部全type while ( t-- ) { // 解分type和namespace tmp = rtypenamespace.exec( types[t] ) || []; // 失掉type type = origType = tmp[1]; // 失掉namespace namespaces = ( tmp[2] || "" ).split( "." ).sort(); // 如果type是undefined,即来本的type是.xxx.xxx之类的命名空间 if ( !type ) { // 从事件表列读取部全事件 for ( type in events ) { // 添加命名空间,删掉除这些事件 jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } // 试尝失掉特别事件 special = jQuery.event.special[ type ] || {}; 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 ]; // 参数mappedTypes存在或当前事件和handleObj中的当前事件同相 if ( ( mappedTypes || origType === handleObj.origType ) && // 并且参数handler不存在,或handler的ID与handleObj的ID同相 ( !handler || handler.guid === handleObj.guid ) && // 并且没有命名空间,或者是handleObj的命名空间子集 ( !tmp || tmp.test( handleObj.namespace ) ) && // 并且没有selector,或者selector与handleObj的selector同相, // 或者selector为"**"(表现恣意)并且handleObj的selector存在 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { // 部全足满则删掉除当前事件象对 handlers.splice( j, 1 ); // 如果handleObj有selector if ( handleObj.selector ) { handlers.delegateCount--; } // 如果特别事件remove存在,则用调special.remove // 应该和special.add对应,前目应该没什么用 if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // 如果存缓中来本存在事件理处象对,且当前没有事件理处象对 // 证明部全在上面循环中删掉除了,就清掉除 // 防止在潜的特别事件理处程序无限递归 if ( origCount && !handlers.length ) { // 则试尝用special.teardown删除事件对handle的绑定 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { // 不成功则应用removeEventListener删除绑定 // 这里虽然还是这么写但实际上就是removeEventListener了 jQuery.removeEvent( elem, type, elemData.handle ); } // 删除存缓中对应事件理处函数表列 delete events[ type ]; } } // 如果存缓events经已空了,该象对没有任何事件绑定了 if ( jQuery.isEmptyObject( events ) ) { // 在存缓中删除handle delete elemData.handle; // 清掉除events jQuery._removeData( elem, "events" ); } };
- 实际上,要主是删除时要判断事件、理处函数、命名空间等是不是匹配,匹配才能删除。
- 还有就是,如果该事件的理处函数队列空了就要需对该事件解绑定。
- 如果改间时的事件表列都空了,那么就将主理处器,事件表列都删掉。
然后剩下的解绑定函数都是由jQuery.fn.off扩展来的。
jQuery.fn.unbind
jQuery.fn.unbind: function( types, fn ) { return this.off( types, null, fn ); };
jQuery.fn.undelegate
jQuery.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.trigger
jQuery.fn.trigger = function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); };
jQuery.fn.trigger方法直接用调jQuery.event.trigger来模拟发消息。
面下的jQuery.fn.triggerHandler也是通过jQuery.event.trigger来模拟发消息。
jQuery.fn.triggerHandler = function( type, data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } };
jQuery.event.trigger
jQuery.event.trigger = function( event, data, elem, onlyHandlers ) { var i, cur, tmp, bubbleType, ontype, handle, special, // 要需触发事件的部全元素队列 eventPath = [ elem || document ], // 指定事件类型 type = event.type || event, // 事件是不是有命名空间,有则分割成组数 namespaces = event.namespace ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // 对于text和comment点节不停止事件理处 if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // 仅对focus/blur事件种变成focusin/out停止理处 // 如果浏览器原生持支focusin/out,则保确当前不触发他们 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } // 如果type有命名空间 if ( type.indexOf(".") >= 0 ) { // 从新组装事件 namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } // 看看是不是要需改成ontype式形 ontype = type.indexOf(":") < 0 && "on" + type; // 看看这个是不是由jQuery.Event成生的例实,否则用jQuery.Event改革 event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); // 对event预理处 event.isTrigger = true; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // 除清数据,以从新应用 event.result = undefined; // 如果事件没有触发元素,则用elem替代 if ( !event.target ) { event.target = elem; } // 如果data为空,则传入理处函数的是event,否则由data和event构成 data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // 试尝通过特别事件停止理处,须要时候退出函数 special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // 如果要需泡冒,特别事件不要需止阻泡冒,且elem不是window象对 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { // 泡冒时是不是要需转成别的事件(用于事件模拟) bubbleType = special.delegateType || type; // 如果不是变形来的foucusin/out事件 if ( !rfocusMorph.test( bubbleType + type ) ) { // 则定义当前元素师父点节 cur = cur.parentNode; } // 遍历自身及部全父点节 for ( ; cur; cur = cur.parentNode ) { // 推入要需触发事件的部全元素队列 eventPath.push( cur ); // 存一下循环中最后一个cur tmp = cur; } // 如果循环中最后一个cur是document,那么事件是要需最后触发到window象对上的 // 将window象对推入元素队列 if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // 触发部全该事件对应元素的事件理处器 i = 0; // 遍历部全元素,并保确事件不要需止阻泡冒 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { // 先肯定事件绑定类型是delegateType还是bindType event.type = i > 1 ? bubbleType : special.bindType || type; // 保确存缓中该元素对应事件中含包事件理处器, // 则出取主理处器(jQuery handle)来制控部全分事件理处器 handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); // 如果主理处器(jQuery handle)存在 if ( handle ) { // 触发理处器 handle.apply( cur, data ); } // 出取原生事件理处器elem.ontype // 比如click事件就是elem.onclick handle = ontype && cur[ ontype ]; // 如果原生事件理处器存在,看看需不要需止阻事件在浏览器上的默许动作 if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); } } // 保存事件类型,因为这时候事件可能变了 event.type = type; // 如果不要需止阻默许动作,当即执行 if ( !onlyHandlers && !event.isDefaultPrevented() ) { // 试尝通过特别事件触发默许动作 if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // 用调一个原生的DOM方法具有同相名称的名称作为事件的目标。 // 例如对于事件click,elem.click()是触发该事件 // 并保确不对window象对止阻默许事件 if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { // 防止我们触发FOO()来触发其默许动作时,onFOO事件又触发了 tmp = elem[ ontype ]; // 清掉除该事件监听 if ( tmp ) { elem[ ontype ] = null; } // 当我们经已将事件向上起泡时,防止同相事件再次触发 jQuery.event.triggered = type; // 触发事件 elem[ type ](); // 成完除清标记 jQuery.event.triggered = undefined; // 事件触发完了,可以把监听从新绑定归去 if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; };
模拟触发为了让事件模型在各浏览器上表现分歧,花了不少的思心。
反过来说,浏览器事件模型表现不分歧,至心折磨人……orz
文章结束给大家分享下程序员的一些笑话语录: 姿势要丰富,经常上百度!