jQuery源码分析之jQuery.even.handlers八问

问题1:delegateCount到底是什么鬼,为什么抓着delegateCount不放?

JS部分

 $("#father").on("click",".class",function(e)//传入了.class的选择器,说明他代理了.class所有的DOM元素
   {
     console.log("child");
   })
    $("#father").on("click",".child1",function(e)//传入了.child1,表示它又代理了.child所有的DOM元素,因此father元素的click事件delegateCount是2
   {
     console.log("child");
   })
    $("#father").on("mouseover",".class",function(e)//传入了.class选择器,表示father元素代理了所有.class的DOM元素,所以mouseover事件的delegateCount是1!
   {
     console.log("child");
   })
   var expando=jQuery.expando;
   var key=$("#father")[0][expando];
   var walhouse=jQuery.cache;
   var data=walhouse[key];
   console.log(data);//仓库里面的数据
HTML部分:

<div id="father" style="background-color:red;height:100px;">
     <div  style="background-color:yellow;height:50px;" class="child">
	 我是华为
	</div>
	 <div  style="background-color:yellow;height:50px;" class="child1">
	 我是华为
	</div>
</div>
通过 该图, 我们知道对于father元素的click事件来说,他代理了selector为.child1和.class的所有的DOM元素,所以click事件的delegateCount是2;对于father的mouseover事件来说,他代理了.class的元素的所有的DOM元素,所以mouseover事件的delegateCount为1!但是通用的handle的elem属性一直就是father元素!
问题2:源码中那些方法调用了jQuery.event.handlers方法?

对于jQuery.event.dispatch是如何被调用的

		jQuery.event.dispatch.apply( eventHandle.elem, arguments ) ://dispatch上下文就是代理对象,也就是上面例子中的father!
jQuery.event.dispatch中调用了jQuery.event.handlers方法

		handlerQueue = jQuery.event.handlers.call( this, event, handlers );//所以handlers上下文也就是代理对象!
note:在dispatch方法中上下文是元素的通用函数的elem属性,也就是代理对象即绑定事件的对象, 而handlers在dispatch中调用,所以他的this上下文也就是elem即 代理对象
问题3:jQuery.event.handlers的作用是什么?

解答:他的作用在于通过把代理对象中的某一类事件组成的handleObj数组进行遍历。当事件触发的时候,从target开始,一直到代理对象,看看每一个对象是否符合这个handleObj中某一个元素的selector,如果符合那么就保存。所以,对于每一个DOM对象,都要循环遍历handleObj数组,看看该DOM对象是否符合该handleObj的选择器!

很显然是双重for循环:

                   for ( ; cur != this; cur = cur.parentNode || this ) {
				/* jshint eqeqeq: true */
				// Don't check non-elements (#13208)
				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
				if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
					matches = [];
					//获取该类型事件代理的选择器的个数,如click代理了.class和.child1
					for ( i = 0; i < delegateCount; i++ ) {
						//获取代理类型的handleObj,每代理一次都会有一个handleObj对象
						handleObj = handlers[ i ];
						// Don't conflict with Object.prototype properties (#13203)
						sel = handleObj.selector + " ";
						if ( matches[ sel ] === undefined ) {
					//如果代理的这个选择器的DOM元素的needsContext是false
					//那么在代理对象下面通过代理时候的选择器再次筛选,也就是筛选出来当前代理对象代理了那些子元素的同类事件!
							//如果needsContext是true,那么看看当前元素是否符合被代理的要求!
							matches[ sel ] = handleObj.needsContext ?
								jQuery( sel, this ).index( cur ) >= 0 :
								jQuery.find( sel, this, null, [ cur ] ).length;
						}
						//如果当前元素符合被代理的要求,那么把handleObj放入数组,准备返回!
						if ( matches[ sel ] ) {
							matches.push( handleObj );
						}
					}
					//返回的数据类型是DOM+满足的回调函数的集合(是一个数组)
					if ( matches.length ) {
						handlerQueue.push({ elem: cur, handlers: matches });
					}
				}
问题4:怎么会出现delegateCount<handlers.length?

if ( delegateCount < handlers.length ) {
		handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });//这些事件是直接绑定到代理对象自身的,仅仅给代理对象
		}
我们要知道,一个DOM可以代理子元素的事件,但是也能够仅仅为自己绑定事件,这时候就会出现这种情况,见下面的例子:

 $("#father").on("click",".class",function(e)//代理
   {
     console.log("child");
   })
    $("#father").click(function()//非代理,这个事件是直接给自己的
	{
	  console.log("father");
	});
    $("#father").on("click",".child1",function(e)//代理
   {
     console.log("child");
   })

    $("#father").on("mouseover",".class",function(e)//代理
   {
     console.log("child");
   })
   var expando=jQuery.expando;
   var key=$("#father")[0][expando];
   var walhouse=jQuery.cache;
   var data=walhouse[key];
   console.log(data);//仓库里面的数据
通过 该图你会发现,对于click来说,他的delegateCount是2,但是却绑定了3个click事件,最后一个事件不是代理的,而是为自己的click事件绑定的函数。所以,对于非代理的事件我们直接slice获取delegateCount剩余的handleObj就可以了!
问题5:通过jQuery.event.handlers处理的数据,返回的格式是什么?

			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
很显然 ,返回的是对象数组,每一个对象的第一个维度是DOM,第二个维度是该DOM满足了那些handleObj具有的选择器。换句话说,一个DOM可能满足多个handleObj具有的选择器,所以他的回调函数是一个数组!
举个例子:

 <div  style="background-color:yellow;height:50px;" class="child child1">
	 我是华为
	</div>
JS部分

 $("#father").on("click",".child",function(e)//代理
   {
     console.log("child");
   })
    $("#father").click(function()//非代理
	{
	  console.log("father");
	});
    $("#father").on("click",".child1",function(e)//代理
   {
     console.log("child");
   })
这时候对于div元素来说,他满足了.child1和.child两个选择器。在father具有的3个handleObj中, 该div元素满足其中handleObj个选择器,所以该elem具有的matches数组中存放了两个handleObj!
问题6:如果事件流到达某一个DOM,但是该DOM不符合任何一个handleObj,那么是否该DOM的也需要返回?

解答:不需要返回

if ( matches.length ) {//如果该DOM不满足任何一个handleObj的selector,那么我们不返回该DOM!
		handlerQueue.push({ elem: cur, handlers: matches });
	}
问题7:handleObj中的needsContext是什么鸟?

	needsContext: selector && jQuery.expr.match.needsContext.test( selector )//我们看看该正则表达式是什么
jQuery.expr.match.needsContext正则表达式

/^[\x20\t\r\n\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\([\x20\t\r\n\f]*((?:-\d)?\d*)[\x20\t\r\n\f]*\)|)(?=[^-]|$)/i
表明,像even,odd等选择器才会满足这个正则表达式,传入着这种selector那么才表明需要上下文

那么如果用户传入了这种选择器,那么我们怎么处理

matches[ sel ] = handleObj.needsContext ?
jQuery( sel, this ).index( cur ) >= 0 :jQuery.find( sel, this, null, [ cur ] ).length;
也就是说, 如果传入了这种选择器,那么我们不过是在判断当前事件流中的DOM元素是否符合这个选择器而已!如果不是这种选择器,我们就用Sizzle来选择,但是 目地也只有一个就是判断当前DOM是否满足该handleObj的选择器,如果满足才会把该handleObj当作该DOM的!
问题8:这个判断是什么意思?

	if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
表示代理了,同时当前事件流所在的元素是Element元素,同时鼠标按键是左键或者非click类型才会受理!注意:左键表示0,1表示中间键,2表示右键!
给出jQuery.event.handle的源码:

handlers: function( event, handlers ) {
		var sel, handleObj, matches, i,
			handlerQueue = [],
			//记录一下当前DOM代理了多少个同类事件!
			delegateCount = handlers.delegateCount,
				//获取冒泡时当前的元素
			cur = event.target;
		// Find delegate handlers
		// Black-hole SVG <use> instance trees (#13180)
		// Avoid non-left-click bubbling in Firefox (#3861)
		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
			/* jshint eqeqeq: false */
			//如果当前DOM不是调用绑定的对象,我们知道dispatch里面的上下文是注册事件的那个元素,而不是发生事件的元素
			//所以,他代理的元素的事件都会冒泡到代理元素上面进行处理。但是,我们应该要知道,为了能在代理的对象的事件
			//处理函数中获取到被代理的所有的事件处理函数,我们需要保存所有的被代理对象的事件处理函数
			for ( ; cur != this; cur = cur.parentNode || this ) {
				/* jshint eqeqeq: true */
				// Don't check non-elements (#13208)
				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
				if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
					matches = [];
					//获取该类型事件代理的选择器的个数,如click代理了.class和.child1
					for ( i = 0; i < delegateCount; i++ ) {
						//获取代理类型的handleObj,每代理一次都会有一个handleObj对象
						handleObj = handlers[ i ];
						// Don't conflict with Object.prototype properties (#13203)
						sel = handleObj.selector + " ";
						if ( matches[ sel ] === undefined ) {
					//如果代理的这个选择器的DOM元素的needsContext是false
				//那么在代理对象下面通过代理时候的选择器再次筛选,也就是筛选出来当前代理对象代理了那些子元素的同类事件!
							//如果needsContext是true,那么看看当前元素是否符合被代理的要求!
							matches[ sel ] = handleObj.needsContext ?
								jQuery( sel, this ).index( cur ) >= 0 :
								jQuery.find( sel, this, null, [ cur ] ).length;
						}
						//如果当前元素符合被代理的要求,那么把handleObj放入数组,准备返回!
						if ( matches[ sel ] ) {
							matches.push( handleObj );
						}
					}
					//返回的数据类型是DOM+满足的回调函数的集合(是一个数组)
					if ( matches.length ) {
						handlerQueue.push({ elem: cur, handlers: matches });
					}
				}
			}
		}
        //执行到这里,表明for循环中cur==this了!但是有些事件不是代理的,而是自己给自己绑定的,这时候就会出现
		//delegateCount< handlers.length的情况!这时候直接把剩余的函数直接以this,也就是代理元素为键返回!
		// Add the remaining (directly-bound) handlers
		if ( delegateCount < handlers.length ) {
			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
		}
		return handlerQueue;
	}

总结:

该方法的处理对象是:代理对象某一类事件所有的handleObj的数组!见该图

该方法的处理时机是:从目标对象向上冒泡到代理对象的过程中!

你可能感兴趣的:(jQuery源码分析之jQuery.even.handlers八问)