内容简介:前言:请
前言:
请 先 回顾下我之前写的一篇文章: 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
是否有值,push handleObj
按照我们的例子来看,当 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 只 push length!==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
覆盖。
(完)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- js事件委托总结
- JQuery7:事件委托
- Kotlin Vocabulary:Kotlin 委托代理
- 通过发布订阅模式实现的事件委托
- BeetleX之XRPC远程委托调用
- 委托、匿名方法到lambda表达式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。