媒介:
请先回忆下我之前写的一篇文章: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 的事宜托付递次:
举例:
(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 的构造以下:
能够看到,
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 的构造以下:
能够看到,
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 的构造以下:
能够看到,
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
掩盖。
(完)