前言:
請先回顧下我以前寫的一篇文章:JavaScript之事件委託node
1、事件委託(委派)
含義:
在#A
上綁定click
事件,可是讓#B
觸發click
事件,至關於在 #B 上假綁定了 click 事件this
也就是說:#B 委託了 click 事件給了 #A(在 #A 上綁定)spa
舉例:prototype
<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 的事件委託順序:code
舉例:對象
(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 被點擊了ip
(2)A 本身綁定了 click 事件,同時 B、C 還委託給 A 綁定 click 事件element
$("#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
覆蓋。
(完)