发现一个小点,先前没有注意的
stopPropagation: function() {
var e = this.originalEvent;
...
if ( e.stopPropagation ) {
e.stopPropagation();
}
jQuery重载stopPropagation函数调用的本地事件对象的stopPropagation函数阻止冒泡。也就是说,阻止冒泡的是当前节点,而不是事件源。
说到触发事件,我们第一反应是使用$(...).click()这种方式触发click事件。这种方式毫无疑问简洁明了,如果能使用这种方式推荐使用这种方式。但是如果是自定义事件呢?比如定义一个$(document).on("chuaClick","#middle",fn);这种情况怎么触发事件?这就要用到$("#middle").trigger("chuaClick")了。
a.触发事件低级API——jQuery.event.trigger
trigger函数对所有类型事件的触发提供了支持。这些事件主要分为两类:普通浏览器事件(包含带有命名空间的事件如"click.chua")、自定义事件。因为要统一处理,所以函数内部实现没有调用.click()这种方式来对普通浏览器事件做捷径处理,而是统一流程。处理过程如下
1.获取要触发的事件(传入的event可能是事件类型而不是事件对象)
event = event[ jQuery.expando ] ? event :new jQuery.Event( type, typeof event === "object" && event );
2.修正浏览器事件(主要有修正事件源)和组合正确的事件处理参数data
if ( type.indexOf(".") >= 0 ) {
//有命名空间的事件触发; 先取出事件处理入口函数handle()使用的事件类型type
namespaces = type.split(".");
type = namespaces.shift();
namespaces.sort();
}
...// 调用者可以传递jQuery.Event对象,普通对象,甚至是字符串
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event );
event.isTrigger = true;
event.namespace = namespaces.join(".");
event.namespace_re = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
null;
// 重置result属性,避免上次的结果残留
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}
// 克隆传参data并将event放在传参data的前面,创建出事件处理入口函数的参数列表,创建后结果可能是[event,data]
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] );
后面这段组合事件处理参数列表data在后面处理时调用
if ( handle ) {
handle.apply( cur, data );
}
3.判断是否是特殊节点对象的的特殊事件,是的话特殊处理
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
这里面需要特殊处理的事件比较少,这里列一下
special: {
click.trigger: function(){ // checkbox, 触发本地事件确保状态正确if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
this.click();
return false;
}
},
focus.trigger: function() { // 触发本地事件保证失焦/聚焦序列正确if ( this !== document.activeElement && this.focus ) {
try {
this.focus();
return false;
} catch ( e ) {
// Support: IE<9
// If we error on focus to hidden element (#1486, #12518),
// let .trigger() run the handlers
}
}
},
blur.trigger: function() {if ( this === document.activeElement && this.blur ) {
this.blur();
return false;
}
}
}
4.从事件源开始遍历父节点直到Window对象,将经过的节点保存(保存到eventPath)下来备用
for ( ; cur; cur = cur.parentNode ) {
eventPath.push( cur );
tmp = cur;
}
// 将window也压入eventPath(e.g., 不是普通对象也不是断开连接的DOM)
if ( tmp === (elem.ownerDocument || document) ) {
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
}
5.循环先前保存的节点,访问节点缓存,如果有对应的事件类型处理队列则取出其绑定的事件(入口函数)进行调用。
// jQuery绑定函数处理:判断节点缓存中是否保存相应的事件处理函数,如果有则执行
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
// 本地绑定处理
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
6. 最后处理浏览器默认事件,比如submit标签的提交表单处理。
// 如果没有人阻止默认的处理,执行之
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
...
}
注意:普通事件加上命名空间仍然属于普通事件,普通调用方式依然其作用。比如$(document).on('click.chua',"#id",fn1).on("click","#id",fn2);当点击“#id”节点的时候fn1依然会被调用。触发指定命名空间事件的唯一方式是trigger:$("#id").trigger("click.chua"),此时只会调用fn1。
从第4、5个步骤可以看到trigger的另外一个巨大作用——模拟冒泡处理。后面会分析到
b. 事件特殊处理jQuery.event.special(主要有事件替代、模拟冒泡)详解
委托设计是基于事件可冒泡的。但是有些事件是不可冒泡的,有的事件在不同的浏览器上支持的冒泡情况不同。还有不同的浏览器支持的事件类型也不尽相同。这些处理主要都被放在jQuery.event.special中。jQuery.event.special对象中保存着为适配特定事件所需的变量和方法。
具体有:
delegateType / bindType (用于事件类型的调整)
setup (在某一种事件第一次绑定时调用)
add (在事件绑定时调用)
remove (在解除事件绑定时调用)
teardown (在所有事件绑定都被解除时调用)
trigger (在内部trigger事件的时候调用)
noBubble
_default
handle (在实际触发事件时调用)
preDispatch (在实际触发事件前调用)
postDispatch (在实际触发事件后调用)
看一下模拟冒泡的函数simulate
simulate: function( type, elem, event, bubble ) {
// 构建一个新的事件以区别先前绑定的事件.
// 新构建的事件避免阻止冒泡, 但如果模拟事件可以阻止默认操作的话,我们做同样的阻止默认操作。
var e = jQuery.extend(
new jQuery.Event(),
event,
{ type: type,
isSimulated: true,
originalEvent: {}
}
);
if ( bubble ) {
jQuery.event.trigger( e, null, elem );
} else {
jQuery.event.dispatch.call( elem, e );
}
if ( e.isDefaultPrevented() ) {
event.preventDefault();
}
}
看到没有,真正模拟冒泡函数是jQuery.event.trigger函数
special第一组
这里面涉及到冒泡处理的问题。
special: {
load: {
//阻止触发image.load事件冒泡到window.load
noBubble: true
},
click: {
//checkbox触发时保证状态正确
trigger: function() {if (...) {this.click();return false;}}
},
focus: {
//触发本当前节点blur/focus事件 确保队列正确
trigger: function() {
if ( this !== document.activeElement && this.focus ) {
try {
this.focus();
return false;
} catch ( e ) {
// IE<9,如果我们错误的让隐藏的节点获取焦点(#1486, #12518),
// 让.trigger()运行处理器
}
}
},
delegateType: "focusin"

