jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

媒介:
回忆下我之前写的一篇文章:JavaScript之事宜托付

一、事宜托付(委派)
寄义:
#A上绑定click事宜,然则让#B触发click事宜,相当于在 #B 上假绑定了 click 事宜

也就是说:#B 托付了 click 事宜给了 #A(在 #A 上绑定)

举例:

<div id="A" style="background-color: deeppink">
  这是A
  
  <div id="B" style="background-color: bisque">
    这是B
  
    <div id="C" style="background-color: aqua">
    这是C
    </div>
  
    <div id="D" style="background-color: blueviolet">
    这是D
    </div>

  </div>
</div>

  //在父元素上绑定click事宜,但只能由子元素触发父元素上绑定的事宜
  $("#A").on("click" ,"#B",function (e) {
    console.log("点击了B,即B托付A的click事宜被点击了")
  })
  $("#A").on("click" ,"#C",function (e) {
    console.log(e,"点击了C,即C托付A的click事宜被点击了")
  })

二、jQuery 的事宜托付递次:

举例:

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

(1)A、B、C 各自绑定了click事宜

 $("#A").on("click" ,function () {
   console.log("A被点击了")
 })

 $("#B").on("click" ,function () {
   console.log("B被点击了")
 })

 $("#C").on("click",function () {
   console.log("C被点击了")
 })

点击 C,会顺次实行 C、B、A 的click事宜

输出效果:
① C 被点击了
② B 被点击了
③ A 被点击了

(2)A 本身绑定了 click 事宜,同时 B、C 还托付给 A 绑定 click 事宜

 $("#A").on("click" ,function () {
   console.log("A被点击了")
 })

 $("#A").on("click" ,"#B",function () {
   console.log("点击了B,即B托付A的click事宜被点击了")
 })

 $("#A").on("click" ,"#C",function () {
   console.log("点击了C,即C托付A的click事宜被点击了")
 })

点击 C,顺次实行 C、B 托付给 A 的 click 事宜,末了实行 A 本身的 click 事宜

输出效果:
① 点击了 C,即 C 托付 A 的 click 事宜被点击了
② 点击了 B,即 B 托付 A 的 click 事宜被点击了
③ A 被点击了

(3)A 本身绑定了 click 事宜,同时 B、C 还托付给 A 绑定 click 事宜,同时 B、C 另有本身的 click 事宜:

 $("#A").on("click" ,function () {
   console.log("A被点击了")
 })

 $("#A").on("click" ,"#B",function () {
   console.log("点击了B,即B托付A的click事宜被点击了")
 })

 $("#A").on("click" ,"#C",function () {
   console.log("点击了C,即C托付A的click事宜被点击了")
 })

 $("#B").on("click" ,function () {
   console.log("B被点击了")
 })

 $("#C").on("click",function () {
   console.log("C被点击了")
 })

点击 C,顺次实行:C 本身的事宜、B 本身的事宜、C 托付给 A 的 click 事宜、B托付给 A 的 click 事宜、A 本身的 click 事宜。

输出效果:
① C 被点击了
② B 被点击了
③ 点击了 C,即 C 托付 A 的 click 事宜被点击了
④ 点击了 B,即 B 托付 A 的 click 事宜被点击了
⑤ A 被点击了

综上,jQuery事宜托付的递次为:
(1)先一致处置惩罚本身、父元素本身绑定的事宜
(2)再一致处置惩罚本身、父元素托付给先人元素的绑定事宜
(3)末了先人元素处置惩罚本身的事宜

精练说,就是:
先处置惩罚子元素托付给本身的事宜,再处置惩罚本身的事宜。

源码:
$().on()—>jQuery.event.add()

jQuery.event = {
    //源码5241行
    //this, types, fn, data, selector

    //#A,'click',function(){console.log('A被点击了')},undefined,undefined
    //#A,'click',function(){点击了C,即C托付A的click事宜被点击了},undefined,#C
    add: function( elem, types, handler, data, selector ) {
      xxx
      ...
      //优先增加托付handler,再增加其他handler
      // Add to the element's handler list, delegates in front
      //delegateCount即托付在#A上的事宜数目
      if ( selector ) {
        //在下标为handlers.delegateCount++的位置插进去托付事宜
        handlers.splice( handlers.delegateCount++, 0, handleObj );
      } else {
        handlers.push( handleObj );
      }
}

剖析:
能够看到,jQuery 是优先增加托付 click 事宜,再增加本身 click 事宜,触发事宜的时刻也是按这个递次。

注重:
以下的例子,点击 E 是不能触发 click 事宜的,由于冒泡冒不到 A 上:

<div id="A" style="background-color: deeppink">
  这是A
</div>

<div id="E" style="background-color: brown">这是E</div>

  $("#A").on("click" ,"#E",function (event) {
    console.log(event,"点击了E,即E托付A的click事宜被点击了")
  })

三、jQuery 绑定事宜上的 event 上的 target、currenttarget 和 delegateTarget 的区分?

target 是触发事宜的对象
delegateTarget 是事宜托付的原对象

而currenttarget分三种状况:
(1)A 在本身有绑定 click 事宜的条件下,C 再去托付 A 绑定 click 事宜

<div id="A" style="background-color: deeppink">
  这是A

  <div id="B" style="background-color: bisque">
    这是B

    <div id="C" style="background-color: aqua">
    这是C
    </div>

    <div id="D" style="background-color: blueviolet">
    这是D
    </div>

  </div>
</div>

  $("#A").on("click" ,function (event) {
    console.log(event,"A被点击了")
  })

  $("#A").on("click" ,"#C",function (event) {
    console.log(event,"点击了C,即C托付A的click事宜被点击了")
  })
  
  $("#C").on("click",function (event) {
    console.log(event,"C被点击了")
  })

点击了C,即 C 托付 A 的 click 事宜被点击了
event 的构造以下:

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

能够看到,
target 是 #C,currenttarget 是 #A,delegateTarget 是 #A

也就是说:
target 是触发 click 事宜的对象 #C,currenttarget 是 #C 托付绑定click事宜的 #A,而且 #A 本身有绑定 click 事宜

② A被点击了
target 是 #A,currenttarget 是 #A,delegateTarget 是 #A

③ C被点击了
target 是 #C,currenttarget 是 #C,delegateTarget 是 #C

(2)A 本身没有绑定 click 事宜,C 托付 A 绑定 click 事宜

<div id="A" style="background-color: deeppink">
  这是A

  <div id="B" style="background-color: bisque">
    这是B

    <div id="C" style="background-color: aqua">
    这是C
    </div>

    <div id="D" style="background-color: blueviolet">
    这是D
    </div>

  </div>
</div>

  $("#A").on("click" ,"#C",function (event) {
    console.log(event,"点击了C,即C托付A的click事宜被点击了")
  })
  
  $("#C").on("click",function (event) {
    console.log(event,"C被点击了")
  })

点击了 C,即 C 托付 A 的 click 事宜被点击了
event 的构造以下:

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

能够看到,
target 是 #C,currenttarget 是 #C,而不是 #A,delegateTarget 是 #A

也就是说:
target 是触发 click 事宜的对象 #C,currenttarget 是 #C,由于 #C 托付 #A 绑定 click 事宜,而且 #A 本身没有绑定 click 事宜

② C被点击了
target是 #C,currenttarget 是 #C,delegateTarget 是 #C

(3)A在本身有绑定click事宜的条件下,C再去托付A绑定click事宜的同时,阻挠冒泡!

<div id="A" style="background-color: deeppink">
  这是A

  <div id="B" style="background-color: bisque">
    这是B

    <div id="C" style="background-color: aqua">
    这是C
    </div>

    <div id="D" style="background-color: blueviolet">
    这是D
    </div>

  </div>
</div>

  $("#A").on("click" ,"#C",function (event) {
    event.stopPropagation()
    console.log(event,"点击了C,即C托付A的click事宜被点击了")
  })

  $("#C").on("click",function (event) {
    console.log(event,"C被点击了")
  })

点击了C,即C托付A的click事宜被点击了
event 的构造以下:

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

能够看到,
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.dispatch
currenttarget在jQuery.event.dispatch中定义,所以我们看jQuery.event.dispatch部份源码:

jQuery.event = {
  //源码5472行
  //nativeEvent即原生MouseEvent
  dispatch: function( nativeEvent ) {
    //猎取handler行列
    handlerQueue = jQuery.event.handlers.call( this, event, handlers );

    //假如没有阻挠冒泡的话,那末
    while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
        event.currentTarget = matched.elem;
    }
  }
  
    //源码5547行
    //组装事宜处置惩罚行列  
    //event是fix过的MouseEvent, handlers  
    handlers: function( event, handlers ) {
      //目的元素
      var cur = 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 < delegateCount; i++ ) {
              handleObj = handlers[ i ];
              //sel就是#C
              // Don't conflict with Object.prototype properties (#13203)
              sel = handleObj.selector + " ";

              if ( matchedSelectors[ sel ] === undefined ) {
                matchedSelectors[ sel ] = handleObj.needsContext ?
                  jQuery( sel, this ).index( cur ) > -1 :
                  //注重:jQuery.find()和jQuery().find()是不一样的
                  jQuery.find( sel, this, null, [ cur ] ).length;
              }

              if ( matchedSelectors[ sel ] ) {
                matchedHandlers.push( handleObj );
              }
            }
          }

            if ( matchedHandlers.length ) {
                handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
           }
      }

     // Add the remaining (directly-bound) handlers
     //#A 
     cur = this;
     //1<2 true
    //1<1 false
     //将除托付事宜的事宜(如本身绑定的事宜)放入handlerQueue中
     if ( delegateCount < handlers.length ) {
        handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
      }
  }

}

剖析:
event.currentTarget—>handlerQueue[ i++ ]—>jQuery.event.handlers

jQuery.event.handlers:
for轮回的意义是:
(1)只需cur不即是this,即#A,就一向轮回
每次轮回:
(2)将matchedHandlers置为[ ]
(3)轮回托付绑定的事宜数目
轮回托付绑定:
(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 又即是 this,即 #A,末了将除托付事宜的事宜(如本身绑定的事宜)放入 handlerQueue 中,cur=#A

再拿例子举,即(2)A 本身没有绑定 click 事宜,C 托付 A 绑定 click 事宜
只要一个 handler,而且是托付 handler,

handlerQueue[
  {
    elem:#C,
    ...
  },
]
//#C
event.currentTarget = handlerQueue[0].elem

(1)A 在本身有绑定 click 事宜的条件下,C 再去托付 A 绑定 click 事宜
有两个 handler

handlerQueue[
  {
    elem:#C,
    ...
  },
  {
    elem:#A,
    ...
  },
]
//#C
event.currentTarget = handlerQueue[0].elem
//#A
event.currentTarget = handlerQueue[1].elem

由于#A只要一个event,所以在轮回handlerQueue[i]时,event.currenttarget终究被#A所掩盖

 while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
        //终究被#A所掩盖
        event.currentTarget = matched.elem;
    }

(3)A在本身有绑定click事宜的条件下,C再去托付A绑定click事宜的同时,阻挠冒泡!
由于!event.isPropagationStopped(),所以event.currentTarget=#C,未被#A掩盖。

《jQuery源码剖析之你并不真的懂事宜托付及target和currenttarget的区分》

(完)

    原文作者:小进进
    原文地址: https://segmentfault.com/a/1190000019419719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞