读jQuery之十四 (触发事件核心方法) |
本文标签:触发事件 在 事件模块的演变 我使用了dispatchEvent(标准) 和fireEvent(IE)来主动触发事件 。如下 复制代码 代码如下: ... dispatch = w3c ? function(el, type){ try{ var evt = document.createEvent(Event); evt.initEvent(type,true,true); el.dispatchEvent(evt); }catch(e){alert(e)}; } : function(el, type){ try{ el.fireEvent(on+type); }catch(e){alert(e)} }; ... jQuery则完全没有用到dispatchEvent/fireEvent方法 。它采用的是另外一种机制 。 jQuery触发事件的核心方法是jQuery.event.trigger 。它提供给客户端程序员使用的触发事件方法有两个:.trigger/.triggerHandler ![]() 一个事件的发生在某些元素中可能会导致两种动作,一个是默认行为,一个是事件handler 。如链接A <a href="http://mail.sina.com.cn" onclick="alert(1);">新浪邮箱</a> 点击后,弹出1(事件handler),点确定跳转(默认行为)到了mail.sina.com.cn 。因此,设计的触发事件的函数要考虑到这两种情况 。 jQuery使用.trigger和.triggerHandler区分了这两种情况: .trigger 执行事件hanlder/执行冒泡/执行默认行为 .triggerHandler 执行事件handler/不冒泡/不执行默认行为 复制代码 代码如下: .trigger/.triggerHandler的源码如下 trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { if ( this[0] ) { return jQuery.event.trigger( type, data, this[0], true ); } }, 可以看出,两者都调用jQuery.event.trigger 。调用时一个没有传true,一个传了 。传了true的triggerHander就表示仅执行事件handler 。 此外还需注意一点区别:.trigger是对jQuery对象集合的操作,而.triggerHandler仅操作jQuery对象的第一个元素 。如下 复制代码 代码如下: <p>p1</p> <p>p1</p> <p>p1</p> <script> $(p).click(function(){alert(1)}); $(p).trigger(click); // 弹3次,即三个p的click都触发了 $(p).triggerHandler(click); // 仅弹1次,即只触发第一个p的click </script> 好了,是时候贴出jQuery.event.trigger的代码了 复制代码 代码如下: trigger: function( event, data, elem, onlyHandlers ) { // Event object or event type var type = event.type || event, namespaces = [], exclusive; ...... } 这就是jQuery.event.trigger的定义,省略了大部分 。下面一一列举 复制代码 代码如下: if ( type.indexOf("!") >= 0 ) { // Exclusive events trigger only for the exact event (no namespaces) type = type.slice(0, -1); exclusive = true; } 这一段是为了处理.trigger(click!)的情形,即触发非命名空间的事件 。变量exclusive挂在事件对象上后在jQuery.event.handle内使用 。举个例子 复制代码 代码如下: function fn1() { console.log(1) } function fn2() { console.log(2) } $(document).bind(click.a, fn1); $(document).bind(click, fn2); $(document).trigger(click!); // 2 为document添加了两个点击事件,一个是具有命名空间的"click.a",一个则没有"click" 。使用trigger时参数click后加个叹号"!" 。从输出结果为2可以看出不触发命名空间的事件 。总结一下: .trigger(click) 触发所有的点击事件 .trigger(click.a) 仅触发“click.a” 的点击事件 .trigger(click!) 触发非命名空间的点击事件 接着看 复制代码 代码如下: if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } 这段就很好理解了,就是对.trigger(click.a)的处理,即对具有命名空间事件的处理 。 接着看 复制代码 代码如下: if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { // No jQuery handlers for this event type, and it cant have inline handlers return; } 对于一些特殊事件如"getData"或对于已经触发过的事件直接返回 。 往下 复制代码 代码如下: event = typeof event === "object" ? // jQuery.Event object event[ jQuery.expando ] ? event : // Object literal new jQuery.Event( type, event ) : // Just the event type (string) new jQuery.Event( type ); 有三种情况 ,event 本身就是jQuery.Event类的实例 ,event是个普通js对象(非jQuery.Event类的实例) ,event是个字符串,如"click" 续 event.type = type; event.exclusive = exclusive; event.namespace = namespaces.join("."); event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); 需要注意exclusive/namespace/namespace_re挂到了event上了,在jQuery.event.handle中可以用到(事件命名空间) 。 往下是 复制代码 代码如下: // triggerHandler() and global events dont bubble or run the default action if ( onlyHandlers || !elem ) { event.preventDefault(); event.stopPropagation(); } onlyHandlers 只在 .triggerHandler用到了,即不触发元素的默认行为,且停止冒泡 。 下面是 复制代码 代码如下: // Handle a global trigger if ( !elem ) { // TODO: Stop taunting the data cache; remove global events and always attach to document jQuery.each( jQuery.cache, function() { // internalKey variable is just used to make it easier to find // and potentially change this stuff later; currently it just // points to jQuery.expando var internalKey = jQuery.expando, internalCache = this[ internalKey ]; if ( internalCache && internalCache.events && internalCache.events[ type ] ) { jQuery.event.trigger( event, data, internalCache.handle.elem ); } }); return; } 这里是个递归调用 。如果没有传elem元素,那么从jQuery.cache里取 。 接着是 复制代码 代码如下: // Dont do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } 属性,文本节点直接返回 。 下面是 复制代码 代码如下: // Clone any incoming data and prepend the event, creating the handler arg list data = data != null ? jQuery.makeArray( data ) : []; data.unshift( event ); 先将参数data放入数组,event对象放在数组的第一个位置 。 接着是 复制代码 代码如下: // Fire event on the current element, then bubble up the DOM tree do { var handle = jQuery._data( cur, "handle" ); event.currentTarget = cur; if ( handle ) { handle.apply( cur, data ); } // Trigger an inline bound script if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { event.result = false; event.preventDefault(); } // Bubble up to document, then to window cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; } while ( cur && !event.isPropagationStopped() ); 这段代码很重要,做了以下事情 ,取handle ,执行 ,执行通过onXXX方式添加的事件(如onclick="fun()") ,取父元素 while循环不断重复这四步以模拟事件冒泡 。直到window对象 。 接下是 复制代码 代码如下: // If nobody prevented the default action, do it now if ( !event.isDefaultPrevented() ) { var old, special = jQuery.event.special[ type ] || {}; if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Cant use an .isFunction)() check here because IE6/7 fails that test. // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. try { if ( ontype && elem[ type ] ) { // Dont re-trigger an onFOO event when we call its FOO() method old = elem[ ontype ]; if ( old ) { elem[ ontype ] = null; } jQuery.event.triggered = type; elem[ type ](); } } catch ( ieError ) {} if ( old ) { elem[ ontype ] = old; } jQuery.event.triggered = undefined; } } 这一段是对于浏览器默认行为的触发 。如form.submit(),button.click()等 。 注意,由于Firefox中链接的安全性限制,jQuery对链接的默认行为都统一为不能触发 。即不能通过.trigger()使链接跳转 。 |