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

来源:互联网 发布:本地域名服务器是什么 编辑:程序博客网 时间:2024/05/12 17:24

问题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和.child1for ( 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和.child1for ( 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) handlersif ( delegateCount < handlers.length ) {handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });}return handlerQueue;}

总结:

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

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

0 0