当前位置: 首页 > Web前端 > HTML5

jQuery源码分析:你真的不明白事件委托和target和currenttarget的区别

时间:2023-04-05 01:08:00 HTML5

前言:请回顾一下我之前写的一篇文章:JavaScript事件委托1.事件委托(delegation)含义:在#绑定点击A上的事件,却让#B触发了点击事件,相当于在#B上假绑定了点击事件。也就是说:#B将点击事件委托给#A(绑定在#A上)例如:ThisisA这是B这是C

ThisisD
//在父元素上绑定了点击事件,但是父元素绑定的事件只能由子元素触发$("#A").on("click","#B",function(e){console.log("B被点击,即B将点击事件委托给A")})$("#A").on("click","#C",function(e){console.log(e,"点击C,即C将被点击的点击事件委托给A")})S??econd,jQuery的事件委托顺序:例子:(1)A、B、C分别绑定点击事件$("#A").on("click",function(){console.log("Awasclicked")})$("#B").on("点击",function(){console.log("B被点击")})$("#C").on("点击",function(){console.log("Cwasclicked")})点击C,会依次执行C、B、A的点击事件输出结果:①C被点击②B被点击③A被点击(2)A自己绑定点击事件,B和C也委托给A绑定点击事件$("#A").on("click",function(){console.log("A被点击")})$("#A").on("点击","#B",function(){console.log("点击了B,即B委托A点击eventisclicked")})$("#A").on("click","#C",function(){console.log("C被点击,即A委托给C的点击事件就是Click")})点击C,依次执行C和B委托给A的点击事件,最后执行A自己的点击事件输出结果:①C被点击,即C委托给A的点击事件被点击②B被点击,即B委托A的点击事件被点击③A被点击(3)A自己绑定点击事件,B和C也委托给A绑定点击事件,B和C也有自己的点击事件:$("#A").on("click",function(){console.log("Awasclicked")})$("#A").on("click","#B",function(){console.log("点击了B,即B委托A的点击事件被点击")})$("#A").on("click","#C",function(){console.log("C被点击,即C委托A的点击事件被点击")})$("#B").on("click",function(){console.log("B被点击")})$("#C").on("点击",function(){console.log("C被点击")})点击C,依次执行:C自己的event,B自己的Event,C委托给A的点击事件,B委托给A的点击事件,A自己的点击事件输出结果:①C被点击②B被点击③C被点击,即C委托A的点击事件被点击④点击B,即B委托A被点击的点击事件⑤A被点击,jQuery事件委托的顺序是:(1)先统一处理自己和父元素绑定的事件(2)再统一处理自己和父元素到祖先元素的绑定事件(3)最后,祖先元素处理自己的事件简单来说,事件就是:先处理子元素委托给自己的事件,再处理自己的事件。源码:$().on()—>jQuery.event.add()jQuery.event={//源码第5241行//this,types,fn,data,selector//#A,'click',function(){console.log('Awasclicked')},undefined,undefined//#A,'click',function(){C被点击,即C委托给A的点击事件被点击},undefined,#Cadd:function(elem,types,handler,data,selector){xxx...//先添加委托处理程序,再添加其他处理程序//添加到元素的处理程序列表中,委托在前面//DelegateCount是委托在#NumberofeventsonAif(selector){//在下标为handlers的位置插入委托事件handlers.delegateCount++handlers.splice(handlers.delegateCount++,0,handleObj);}else{handlers.push(handleObj);}}分析:可以看出jQuery是先添加委托的点击事件,然后再添加自己的点击事件,事件触发时也是按照这个顺序。注意:在下面的例子中,点击E不会触发点击事件,因为冒泡无法到达A:ThisisA这是E$("#A").on("click","#E",function(event){console.log(event,"Eisclicked,即E委托的点击事件被点击")})三、jQuery绑定事件上target、currenttarget和delegateTarget的区别?target是触发事件的对象。delegateTarget是事件委托的原始对象,currenttarget分为三种情况:(1)在A自身有绑定点击事件的情况下,C委托A绑定点击事件这是A这是B这是C这是D$("#A").on("click",function(event){console.log(event,"Awasclicked")})$("#A").on("click","#C",function(event){console.log(event,"C被点击,即C委托A的点击事件")})$("#C").on("click",function(event){console.log(event,"Cwasclicked")})①点击C,即C将要点击的A的点击事件委托给A。事件结构如下:可以看到,target是#C,currenttarget是#A,delegateTarget是#A,也就是说:target是触发点击事件的对象#C,而currenttarget是#C委托绑定点击事件的#A,而#A本身也有绑定点击事件②A被点击。target是#A,currenttarget是#A,delegateTarget是#A③C被点击的target是#C,currenttarget是#C,delegateTarget是#C(2)A本身没有绑定点击事件,C委托给A绑定点击事件"style="background-color:aqua">这是C这是D$("#A").on("click","#C",function(event){console.log(event,"C被点击,即C委托A点击的点击事件")})$("#C").on("点击",function(event){console.log(event,"C被点击")})①点击C,即C将A的点击事件委托给点击。事件结构如下:可以看到,target是#C,currenttarget是#C,不是#A,delegateTarget是#A也就是说:target是trigger点击事件#C的对象,currenttarget为#C,因为#C委托#A绑定点击事件,而#A本身不绑定点击事件②C被点击。target为#C,currenttarget为#C,delegateTarget为#C(3)在A自身有绑定点击事件的情况下,C会委托A绑定点击事件,同时防止冒泡!这是A这是B这是C这是D$("#A").on("click","#C",function(event){event.stopPropagation()console.log(event,"C被点击,即C委托A的点击事件被点击")})$("#C").on("click",function(event){console.log(event,"Cwasclicked")})①点击C,即C委托A的点击事件为clicked事件结构如下:可以看到,target是#C,currenttarget是#C,不是#A,delegateTarget是#A②C被点击了,target是#C,currenttarget是#C,delegateTarget是#C。为什么会这样?下面分析一下jQuery源码:$().on()—>jQuery.event.add()—>elem.addEventListener(type,eventHandle),eventHandle—>jQuery。event.dispatchcurrturtarget定义在jQuery.event.dispatch中,所以我们看jQuery.event.dispatch的部分源码:jQuery.event={//源码5472行//nativeEvent是原生的MouseEventdispatch:function(nativeEvent){//获取处理队列handlerQueue=jQuery.event.handlers.call(this,event,handlers);//如果冒泡没有被阻塞,那么while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){event.currentTarget=matched.elem;}}//源代码第5547行//程序集事件处理队列//事件是固定的MouseEvent,handlershandlers:function(event,handlers){//目标元素varcur=event.target;for(;cur!==this;cur=cur.parentNode||this){if(cur.nodeType===1&&!(event.type==="click"&&cur.disabled===true)){matchedHandlers=[];matchedSelectors={};for(i=0;i-1://注意:jQuery.find()和jQuery().find()不一样jQuery.find(sel,this,null,[cur])。长度;}if(matchedSelectors[sel]){matchedHandlers.push(handleObj);}}}if(matchedHandlers.length){handlerQueue.push({elem:cur,handlers:matchedHandlers});}}//添加剩余的(直接绑定的)处理程序//#Acur=this;//1<2true//1<1false//将委托事件以外的事件(例如绑定到自身的事件)放入handlerQueueif(delegateCounthandlerQueue[i++]—>jQuery.event.handlersjQuery.event.handlers:for循环的意思:(1)只要cur不等于这个,即#A,只要不断循环每个循环:(2)将matchedHandlers设置为[](3)循环委托绑定的事件数Bindingloopdelegate:(4)MatchedHandlers取决于handleObj.selector是否有值,根据我们的例子pushhandleObj,当cur=event.target,cur=#C时,则进入冒泡循环,然后进入委托事件循环,关键是:jQuery.find(),当cur=#C时,matchedSelectors[sel]=jQuery.find(sel,this,null,[cur]).length=1但是当cur=#B(冒泡循环)时,matchedSelectors[sel]=0,也就是说jQuery.find()和$()是不一样的。find,冒泡找cur元素!所以matchedHandlers只pushlength!==0委托事件,所以cur是#C(新循环中的当前值)然后cur=this;cur等于这个,也就是#A,最后把委托事件以外的事件(比如自己绑定的事件)放到handlerQueue中,cur=#A再举个例子,就是(2)A本身没有绑定点击事件,C委托A绑定点击事件,只有一个handler,而且是delegatehandler,handlerQueue[{elem:#C,...},]//#Cevent.currentTarget=handlerQueue[0].elem(1)在A自身有绑定点击事件的情况下,C再委托A绑定点击事件。有两个handlerhandlerQueue[{elem:#C,...},{elem:#A,...},]//#Cevent.currentTarget=handlerQueue[0].elem//#Aevent.currentTarget=handlerQueue[1].elem因为#A只有一个事件,所以在循环handlerQueue[i]时,event.currenttarget最终被#A替换Overridewhile((matched=handlerQueue[i++])&&!event.isPropagationStopped()){//最后被#Aevent.currentTarget=matched.elem覆盖;}(3)A自己有绑定点击事件的条件接下来C委托A绑定点击事件,同时防止冒泡!由于!event.isPropagationStopped(),event.currentTarget=#C,不在#A范围内。(超过)