我们知道任何浏览器捕获都是从外层到精确的节点的,所有的focusin事件都会被捕获到,然后执行handler函数(里面是jQuery.event.simulate函数,源码略)。其他事件绑定则进入if分支将事件直接绑定到elem上
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } }
special第二组:mouseenter/mouseleave
//使用mouseover/out和事件时机检测创建mouseenter/leave事件 jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; //对于mousenter/leave,当related在target外面的时候才调用handler //参考: 当鼠标离开/进入浏览器窗口的时候是没有relatedTarget的 if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; });
需要注意的是只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。对应mouseleave这样的话,mouseenter子元素不会反复触发事件,否则在IE中经常有闪烁情况发生
使用mouseover/out和事件时机检测创建mouseenter/leave事件有个关键的判断
if ( !related || (related !== target && !jQuery.contains( target, related )) )
其中!jQuery.contains( target, related )表示related在target外面。我们使用图例来解释
我们假设处理的是mouseenter事件,进入target。
鼠标从related到target,很明显related在target外面,所以当鼠标移动到target的时候满足条件,调用处理。
现在反过来,很明显related在target里面,那么鼠标之前就处于mouseenter状态(意味着之前就进行了mouseenter处理器处理),避免重复调用当然是不进行任何处理直接返回了。
我们假设处理的是mouseleave事件,离开target。
鼠标从target到related,很明显related在target里面,所以当鼠标移动到related的时候依然么有离开target,不做处理。
鼠标从target到related,很明显related在target外面,所以当鼠标移动到related的时候已经离开了target的范围,做处理。
special第三组:submit和change
主要是ie下submit不能冒泡的处理
jQuery.event.special.submit主要有一下几个特征
setup
postDispatch
teardown
根据添加事件的代码可知添加事件的时候如果符合条件则会调用setup来添加事件
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false )
jQuery在ie下模拟submit事件以click和keypress替代,只不过是添加了命名空间来区别和普通click和keypress事件。
setup: function() { ... jQuery.event.add( this, "click._submit keypress._submit", function( e ) { var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !jQuery._data( form, "submitBubbles" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); jQuery._data( form, "submitBubbles", true ); } }); },
在事件调用过程中(dispatch)会调用postDispatch来处理
if ( special.postDispatch ) { special.postDispatch.call( this, event ); } postDispatch中调用simulate完成事件处理 postDispatch: function( event ) { // If form was submitted by the user, bubble the event up the tree if ( event._submit_bubble ) { delete event._submit_bubble; if ( this.parentNode && !event.isTrigger ) { jQuery.event.simulate( "submit", this.parentNode, event, true ); } } },
teardown用在删除事件绑定中
ie下change事件的处理和submit类似,事件使用beforeactivate替代来监听,处理函数变成了handle,在事件分发(dispatch)中执行代码
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args );
主要源码如下