jquery触发事件

Snandy

Stop, thinking is the essence of progress.

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

触发事件,或称模拟用户动作。比如点击,我们可以用代码去模拟用户点击,达到的效果与真实的鼠标点击是一样的。在  事件模块的演变 我使用了dispatchEvent(标准) 和fireEvent(IE)来主动触发事件。如下
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
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

jquery触发事件

 

一个事件的发生在某些元素中可能会导致两种动作,一个是默认行为,一个是事件handler。如链接A

?
1
< 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的源码如下

?
1
2
3
4
5
6
7
8
9
10
11
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对象的第一个元素。如下

?
1
2
3
4
5
6
7
8
< 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的代码了

?
1
2
3
4
5
6
7
8
9
trigger: function ( event, data, elem, onlyHandlers ) {
     // Event object or event type
     var  type = event.type || event,
         namespaces = [],
         exclusive;
 
         ......
 
}

这就是jQuery.event.trigger的定义,省略了大部分。下面一一列举

?
1
2
3
4
5
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内使用。举个例子

?
1
2
3
4
5
6
7
8
9
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!')  触发非命名空间的点击事件

 

接着看

?
1
2
3
4
5
6
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 can't have inline handlers
     return ;
}

对于一些特殊事件如"getData"或对于已经触发过的事件直接返回。

 

往下

?
1
2
3
4
5
6
7
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 );

有三种情况

1,event 本身就是jQuery.Event类的实例

2,event是个普通js对象(非jQuery.Event类的实例)

3,event是个字符串,如"click"

 

?
1
2
3
4
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
2
3
4
5
// triggerHandler() and global events don't bubble or run the default action
if  ( onlyHandlers || !elem ) {
     event.preventDefault();
     event.stopPropagation();
}

 

onlyHandlers 只在 .triggerHandler用到了,即不触发元素的默认行为,且停止冒泡。

 

下面是

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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里取。

 

接着是

?
1
2
3
4
// Don't do events on text and comment nodes
if  ( elem.nodeType === 3 || elem.nodeType === 8 ) {
     return ;
}

属性,文本节点直接返回。

 

下面是

?
1
2
3
// Clone any incoming data and prepend the event, creating the handler arg list
data = data != null  ? jQuery.makeArray( data ) : [];
data.unshift( event );

先将参数data放入数组,event对象放在数组的第一个位置。

接着是

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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() );

这段代码很重要,做了以下事情

1,取handle

2,执行

3,执行通过onXXX方式添加的事件(如onclick="fun()")

4,取父元素

while循环不断重复这四步以模拟事件冒泡。直到window对象。

 

接下是

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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.
         // Can't 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 ] ) {
                 // Don't 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()使链接跳转。

 

 
 
分类:  jQuery

你可能感兴趣的:(jquery)