当使用类似以下代码时,
$(function(){
$("#hr_three").click(function(event){
alert('阻止时间冒泡');
event.stopPropagation();
});
});
<form id="form1">
<div id="divOne" onclick="alert('我是最外层');">
<div id="divTwo" onclick="alert('我是中间层!')">
<a id="hr_three" onclick="alert('我是最里层!')">点击我</a>
</div>
</div>
</form>
jquery会将$("#hr_three")元素与click事件进行绑定,首先jquery会进行事件注册,如下代码:
// 对以下所有事件进行绑定
jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu" ).split( " " ),
function( i, name ) {
// Handle event binding
// 将事件属性添加到jQuery对象
jQuery.fn[ name ] = function( data, fn ) {
// 根据调用参数数量进行不同调用,本例中调用this.on方法,也就是jQuery对象的on方法
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
} );
// 对jQuery.fn进行方法功能增强
jQuery.fn.extend( {
on: function( types, selector, data, fn ) {
// 调用全局on方法
return on( this, types, selector, data, fn );
}
...
}
function on( elem, types, selector, data, fn, one ) {
...
// elem参数指向jQuery对象,调用jQuery对象each方法
return elem.each( function() {
jQuery.event.add( this, types, fn, data, selector );
} );
}
jQuery.fn = jQuery.prototype = {
...
each: function( callback ) {
// each方法是由elem实例调用,因此this指向elem, 这里最终调用jQuery扩展的each方法,如下段代码
return jQuery.each( this, callback );
},
...
}
jQuery.extend( {
...
each: function( obj, callback ) {
var length, i = 0;
// 判断是否是类数组,有length属性并且带有length - 1属性则为类数组,在init方法增强this对象时,已将 //this对象设置了这两个属性,因此增强后的this对象为类数组
if ( isArrayLike( obj ) ) {
/**
obj为jQuery对象,此对象是init方法返回的this,this为针对不同元素增强后的jQuery对象,下面代码是在init方法中对jQuery对象进行了属性增强,增加了长度属性以及0属性,
if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object
this.length = 1;
this[ 0 ] = elem;
}
this.context = document;
this.selector = selector;
return this;
*/
length = obj.length;
for ( ; i < length; i++ ) {
// 方法回调,更改回调方法上下文环境为obj[i],实际上obj[i]就是init方法的this对象的0属性, // this[ 0 ] = elem; elem对象就是html的dom元素
// elem = document.getElementById( match[ 2 ] ); 因此回调方法的上下文环境就为dom元素, // 本例中就是a元素,即document.getElementById("hr_three")
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
} else {
for ( i in obj ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
}
return obj;
},
...
});
最终调用回调方法
// this为dom元素,types为click, fn为触发方法
jQuery.event.add( this, types, fn, data, selector );
在jQuery.event.add 方法中定义事件处理方法
if ( !( eventHandle = elemData.handle ) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
};
}
...
// 生成处理对象
handleObj = jQuery.extend( {
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join( "." )
}, handleObjIn );
// 将处理对象存入到handlers数组
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
...
对elem增加click事件监听,elem为dom元素(本例中为id是hr_three的a元素)
if ( elem.addEventListener ) {
// eventHandle是本方法中定义的事件处理方法,见上段代码
elem.addEventListener( type, eventHandle );
}
至此,a元素已经绑定了click事件,当a元素产生点击事件时,会触发eventHandle指向的方法。
// eventHandle指向的方法参数e为pointerEvent对象
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
// elem为a元素,arguments为调用参数数组,元素0为参数e
// elem为jQuery.event.dispatch方法的上下文环境,arguments[0]为方法的唯一参数
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
};
可以通过以下代码模拟元素增加监听效果,监听方法参数e就是pointerEvent对象:
var elem = document.getElementById("hr_three");
elem.addEventListener( "click", function(e) {
alert('阻止事件冒泡');
e.stopPropagation();
console.log(e);
} );
继续jQuery.event.dispatch方法
jQuery.event = {
...
// event参数为pointerEvent对象
dispatch: function( event ) {
// 将pointerEvent对象转换为jQuery.Event对象
// Make a writable jQuery.Event from the native event object
event = jQuery.event.fix( event );
// 取得处理对象数组
handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
args[ 0 ] = event;
// 将处理对象数组放入处理队列属性中
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
...
while ( ( handleObj = matched.handlers[ j++ ] ) &&
!event.isImmediatePropagationStopped() ) {
...
/**
调用处理方法,方法上下文为a元素,参数为jQuery.Event对象,处理方法event参数实际为args[0]指向的jQuery.Event对象,stopPropagation()方法为jQuery.Event实例方法,其中会将本实例的isPropagationStopped属性设置为true,因此再次外部while循环时,event.isPropagationStopped()即为true了。
function(event){
alert('阻止时间冒泡');
event.stopPropagation();
}
*/
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
handleObj.handler ).apply( matched.elem, args );
...
}
...
}
},
...
}
到此,一个完整的jQuery事件绑定及触发调用的整个过程就完成了。