jquery源码分析之jQuery事件绑定
当使用类似以下代码时,
$(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事件绑定及触发调用的整个过程就完成了。
相关推荐
瓜牛呱呱 2020-11-12
柳木木的IT 2020-11-04
yifouhu 2020-11-02
lei0 2020-11-02
源码zanqunet 2020-10-28
源码zanqunet 2020-10-26
一叶梧桐 2020-10-14
码代码的陈同学 2020-10-14
lukezhong 2020-10-14
lzzyok 2020-10-10
anchongnanzi 2020-09-21
clh0 2020-09-18
changcongying 2020-09-17
星辰大海的路上 2020-09-13
abfdada 2020-08-26
mzy000 2020-08-24
shenlanse 2020-08-18
zhujiangtaotaise 2020-08-18
xiemanR 2020-08-17