前言:
請先回顧下我以前寫的一篇文章:JavaScript之事件委託javascript
1、事件委託(委派)
含義:
在#A
上綁定click
事件,可是讓#B
觸發click
事件,至關於在 #B 上假綁定了 click 事件php
也就是說:#B 委託了 click 事件給了 #A(在 #A 上綁定)java
舉例:node
<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事件被點擊了")
})
複製代碼
2、jQuery 的事件委託順序:app
舉例:ui
(1)A、B、C 各自綁定了click
事件this
$("#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
事件lua
輸出結果:
① C 被點擊了
② B 被點擊了
③ A 被點擊了spa
(2)A 本身綁定了 click 事件,同時 B、C 還委託給 A 綁定 click 事件prototype
$("#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事件被點擊了")
})
複製代碼
3、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
覆蓋。
(完)