Discuz教程网

读jQuery之十四 (触发事件核心方法)

[复制链接]
authicon dly 发表于 2011-9-8 16:35:53 | 显示全部楼层 |阅读模式
在 事件模块的演变 我使用了dispatchEvent(标准) 和fireEvent(IE)来主动触发事件。如下
  1. ...
  2. dispatch = w3c ?
  3. function(el, type){
  4. try{
  5. var evt = document.createEvent('Event');
  6. evt.initEvent(type,true,true);
  7. el.dispatchEvent(evt);
  8. }catch(e){alert(e)};
  9. } :
  10. function(el, type){
  11. try{
  12. el.fireEvent('on'+type);
  13. }catch(e){alert(e)}
  14. };
  15. ...
复制代码

jQuery则完全没有用到dispatchEvent/fireEvent方法。它采用的是另外一种机制。
jQuery触发事件的核心方法是jQuery.event.trigger。它提供给客户端程序员使用的触发事件方法有两个:.trigger/.triggerHandler

一个事件的发生在某些元素中可能会导致两种动作,一个是默认行为,一个是事件handler。如链接A
<a href="http://mail.sina.com.cn">新浪邮箱</a>
点击后,弹出1(事件handler),点确定跳转(默认行为)到了mail.sina.com.cn。因此,设计的触发事件的函数要考虑到这两种情况。
jQuery使用.trigger和.triggerHandler区分了这两种情况:
.trigger 执行事件hanlder/执行冒泡/执行默认行为
.triggerHandler 执行事件handler/不冒泡/不执行默认行为
  1. .trigger/.triggerHandler的源码如下
  2. trigger: function( type, data ) {
  3. return this.each(function() {
  4. jQuery.event.trigger( type, data, this );
  5. });
  6. },
  7. triggerHandler: function( type, data ) {
  8. if ( this[0] ) {
  9. return jQuery.event.trigger( type, data, this[0], true );
  10. }
  11. },
复制代码

可以看出,两者都调用jQuery.event.trigger。调用时一个没有传true,一个传了。传了true的triggerHander就表示仅执行事件handler。
此外还需注意一点区别:.trigger是对jQuery对象集合的操作,而.triggerHandler仅操作jQuery对象的第一个元素。如下
  1. <p>p1</p>
  2. <p>p1</p>
  3. <p>p1</p>
  4. <script>
  5. $('p').click(function(){alert(1)});
  6. $('p').trigger('click'); // 弹3次,即三个p的click都触发了
  7. $('p').triggerHandler('click'); // 仅弹1次,即只触发第一个p的click
  8. </script>
复制代码

好了,是时候贴出jQuery.event.trigger的代码了
  1. trigger: function( event, data, elem, onlyHandlers ) {
  2. // Event object or event type
  3. var type = event.type || event,
  4. namespaces = [],
  5. exclusive;
  6. ......
  7. }
复制代码

这就是jQuery.event.trigger的定义,省略了大部分。下面一一列举
  1. if ( type.indexOf("!") >= 0 ) {
  2. // Exclusive events trigger only for the exact event (no namespaces)
  3. type = type.slice(0, -1);
  4. exclusive = true;
  5. }
复制代码

这一段是为了处理.trigger('click!')的情形,即触发非命名空间的事件。变量exclusive挂在事件对象上后在jQuery.event.handle内使用。举个例子
  1. function fn1() {
  2. console.log(1)
  3. }
  4. function fn2() {
  5. console.log(2)
  6. }
  7. $(document).bind('click.a', fn1);
  8. $(document).bind('click', fn2);
  9. $(document).trigger('click!'); // 2
复制代码

为document添加了两个点击事件,一个是具有命名空间的"click.a",一个则没有"click"。使用trigger时参数click后加个叹号"!"。从输出结果为2可以看出不触发命名空间的事件。总结一下:
.trigger('click') 触发所有的点击事件
.trigger('click.a') 仅触发“click.a” 的点击事件
.trigger('click!') 触发非命名空间的点击事件
接着看
  1. if ( type.indexOf(".") >= 0 ) {
  2. // Namespaced trigger; create a regexp to match event type in handle()
  3. namespaces = type.split(".");
  4. type = namespaces.shift();
  5. namespaces.sort();
  6. }
复制代码

这段就很好理解了,就是对.trigger('click.a')的处理,即对具有命名空间事件的处理。
接着看
  1. if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
  2. // No jQuery handlers for this event type, and it can't have inline handlers
  3. return;
  4. }
复制代码

对于一些特殊事件如"getData"或对于已经触发过的事件直接返回。
往下
  1. event = typeof event === "object" ?
  2. // jQuery.Event object
  3. event[ jQuery.expando ] ? event :
  4. // Object literal
  5. new jQuery.Event( type, event ) :
  6. // Just the event type (string)
  7. 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中可以用到(事件命名空间)。
往下是
  1. // triggerHandler() and global events don't bubble or run the default action
  2. if ( onlyHandlers || !elem ) {
  3. event.preventDefault();
  4. event.stopPropagation();
  5. }
复制代码

onlyHandlers 只在 .triggerHandler用到了,即不触发元素的默认行为,且停止冒泡。
下面是
  1. // Handle a global trigger
  2. if ( !elem ) {
  3. // TODO: Stop taunting the data cache; remove global events and always attach to document
  4. jQuery.each( jQuery.cache, function() {
  5. // internalKey variable is just used to make it easier to find
  6. // and potentially change this stuff later; currently it just
  7. // points to jQuery.expando
  8. var internalKey = jQuery.expando,
  9. internalCache = this[ internalKey ];
  10. if ( internalCache && internalCache.events && internalCache.events[ type ] ) {
  11. jQuery.event.trigger( event, data, internalCache.handle.elem );
  12. }
  13. });
  14. return;
  15. }
复制代码

这里是个递归调用。如果没有传elem元素,那么从jQuery.cache里取。
接着是
  1. // Don't do events on text and comment nodes
  2. if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
  3. return;
  4. }
复制代码

属性,文本节点直接返回。
下面是
  1. // Clone any incoming data and prepend the event, creating the handler arg list
  2. data = data != null ? jQuery.makeArray( data ) : [];
  3. data.unshift( event );
复制代码

先将参数data放入数组,event对象放在数组的第一个位置。
接着是
  1. // Fire event on the current element, then bubble up the DOM tree
  2. do {
  3. var handle = jQuery._data( cur, "handle" );
  4. event.currentTarget = cur;
  5. if ( handle ) {
  6. handle.apply( cur, data );
  7. }
  8. // Trigger an inline bound script
  9. if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) {
  10. event.result = false;
  11. event.preventDefault();
  12. }
  13. // Bubble up to document, then to window
  14. cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window;
  15. } while ( cur && !event.isPropagationStopped() );
复制代码

这段代码很重要,做了以下事情
,取handle
,执行
,执行通过onXXX方式添加的事件(如onclick="fun()")
,取父元素
while循环不断重复这四步以模拟事件冒泡。直到window对象。
接下是
  1. // If nobody prevented the default action, do it now
  2. if ( !event.isDefaultPrevented() ) {
  3. var old,
  4. special = jQuery.event.special[ type ] || {};
  5. if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) &&
  6. !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
  7. // Call a native DOM method on the target with the same name name as the event.
  8. // Can't use an .isFunction)() check here because IE6/7 fails that test.
  9. // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch.
  10. try {
  11. if ( ontype && elem[ type ] ) {
  12. // Don't re-trigger an onFOO event when we call its FOO() method
  13. old = elem[ ontype ];
  14. if ( old ) {
  15. elem[ ontype ] = null;
  16. }
  17. jQuery.event.triggered = type;
  18. elem[ type ]();
  19. }
  20. } catch ( ieError ) {}
  21. if ( old ) {
  22. elem[ ontype ] = old;
  23. }
  24. jQuery.event.triggered = undefined;
  25. }
  26. }
复制代码

这一段是对于浏览器默认行为的触发。如form.submit(),button.click()等。
注意,由于Firefox中链接的安全性限制,jQuery对链接的默认行为都统一为不能触发。即不能通过.trigger()使链接跳转。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x



上一篇:jQuery页面滚动浮动层智能定位实例代码
下一篇:js 函数的副作用分析
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1314学习网 ( 浙ICP备10214163号 )

GMT+8, 2025-5-2 04:54

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表