摘要:一起源方法最终是用绑定事件的而方法正是等于二作用触发绑定的事件的处理程序源码源码行即原生触发事件的处理程序修正对象获取事件的处理程序集合,结构如下从数据缓存中获取事件处理集合即目标元素委托目标这段代码压根不会执行,因为全局搜索没找到结构
一、起源
jQuery.event.add()方法最终是用addEventListener绑定事件的:
elem.addEventListener( type, eventHandle )
而eventHandle方法正是等于jQuery.event.dispatch():
if ( !( eventHandle = elemData.handle ) ) { eventHandle = elemData.handle = function( e ) { return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; }
二、$.event.dispatch()
作用:
触发绑定的事件的处理程序
源码:
//源码5472行 //nativeEvent即原生MouseEvent //触发事件的处理程序 dispatch: function( nativeEvent ) { //修正event对象 // Make a writable jQuery.Event from the native event object var event = jQuery.event.fix( nativeEvent ); console.log(event,"event5479") var i, j, ret, matched, handleObj, handlerQueue, args = new Array( arguments.length ), //获取click事件的处理程序集合,结构如下: //[ // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1}, // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2}, // delegateCount:0, //] //从数据缓存中获取事件处理集合 handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], //click:{ // trigger:{}, // _default:{} //} special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[ 0 ] = event; for ( i = 1; i < arguments.length; i++ ) { args[ i ] = arguments[ i ]; } //this即目标元素 //delegateTarget:委托目标 event.delegateTarget = this; //这段代码压根不会执行,因为全局搜索没找到preDispatch // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers //结构如下 //[{ // elem:xx, // handlers:[ // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1}, // {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2}, // ] //}] //获取handler队列 handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; //没有执行stopPropagation()的话 console.log(handlerQueue,"handlerQueue5525") //先判断有没有冒泡 //再判断有没有阻止剩下的handler执行 while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { console.log(matched,"matched5542") event.currentTarget = matched.elem; j = 0; //handleObj即单个事件处理程序 //没有执行stopImmediatePropagation()的话 //依次执行每一个handler while ( ( handleObj = matched.handlers[ j++ ] ) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or 2) have namespace(s) // a subset or equal to those in the bound event (both can have no namespace). if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { //通过循环将为event添加handleObj和handleObj.data event.handleObj = handleObj; event.data = handleObj.data; //关键代码,执行事件处理程序handler ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || handleObj.handler ).apply( matched.elem, args ); if ( ret !== undefined ) { //event.result赋值ret if ( ( event.result = ret ) === false ) { //阻止默认行为 event.preventDefault(); //阻止冒泡 event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } console.log(handlers,"event5587") //undefined return event.result; },
解析:
(1)jQuery.event.fix()
作用:
将原生事件对象MouseEvent修正(fix)成jQuery的event对象
源码:
//源码5700行 fix: function( originalEvent ) { //如果存在属性id则原样返回(因为已处理成jQueryEvent) return originalEvent[ jQuery.expando ] ? originalEvent : new jQuery.Event( originalEvent ); },
解析:
可以看到fix的本质是新建一个event对象,再看jQuery.Event()方法
(2)jQuery.Event()
源码:
//click,false //修正event对象 //源码5777行 //src即MouseEvent jQuery.Event = function( src, props ) { // Allow instantiation without the "new" keyword if ( !( this instanceof jQuery.Event ) ) { return new jQuery.Event( src, props ); } // Event object //src.type=click if ( src && src.type ) { //MouseEvent this.originalEvent = src; //click this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = src.defaultPrevented || src.defaultPrevented === undefined && // Support: Android <=2.3 only src.returnValue === false ? returnTrue : returnFalse; // Create target properties // Support: Safari <=6 - 7 only // Target should not be a text node (#504, #13143) this.target = ( src.target && src.target.nodeType === 3 ) ? src.target.parentNode : src.target; this.currentTarget = src.currentTarget; this.relatedTarget = src.relatedTarget; // Event type } else { //click this.type = src; } // Put explicitly provided properties onto the event object //false if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn"t have one this.timeStamp = src && src.timeStamp || Date.now(); // Mark it as fixed //修正的标志 this[ jQuery.expando ] = true; };
解析:
简单来说,就是把原生event事件上的常用属性赋值到了jQuery的event上
$("#A").on("click" ,function (event) { //这个就是jQuery.Event()构建出的event console.log(event,"A被点击了") })
jQuery的event结构如下:
//click的event就是jQuery.Event jQuery.Event{ handleObj{ data:undefined, guid: 2, handler:function(){console.log("A被点击了")}, namespace: "clickA", origType: "click", selector: "#B", type: "click.clickA", }, originalEvent:{ //就是MouseEvent }, target:div#B, type: "click", delegateTarget: div#A, //fix 的标志 jQuery331087940272164138: true, currentTarget: div#A, isDefaultPrevented:xxx, timeStamp:Date.now(), isDefaultPrevented:function(){return false} }
注意下originalEvent 和jQuery.extend( this, props )
前者就是原生MouseEvent,只是将原生event作为jQuery.event的originalEvent属性了;
后者是扩展属性,如果开发者想额外加入自定义属性的话。
(3)dataPriv.get( this, "events" )
注意:
jQuery的数据缓存里的events和上面说的event是不同的
数据缓存的events是用来结构如下:
{ click:[ { type: "click", origType: "click", data: undefined, handler: function(){console.log("B委托A绑定click事件")}, guid: 1, namespace: "", needsContext: undefined, selector: #B, }, { type: "click", origType: "click", data: undefined, handler: function(){console.log("A绑定click事件")}, guid: 2, namespace: "", needsContext: undefined, selector: undefined, }, //事件委托的数量 delegateCount:1, ], focus:[ { type: "focus", origType: "focus", data: undefined, handler: function(){console.log("A绑定focus事件")}, guid: 3, namespace: "", needsContext: undefined, selector: undefined, }, delegateCount:0, ], }
(4) jQuery.event.handlers
作用:
获取handler队列
源码:
jQuery.event = { //源码5547行 //组装事件处理队列 //event是fix过的MouseEvent, handlers handlers: function( event, handlers ) { var i, handleObj, sel, matchedHandlers, matchedSelectors, handlerQueue = [], //0 delegateCount = handlers.delegateCount, //目标元素 cur = event.target; //handlers,第一个handler是委托事件,第二个handler是自身事件 // Find delegate handlers if ( delegateCount && // Support: IE <=9 // Black-hole SVG
解析:
注意下这个双层循环,目的是把每一层的委托事件的集合push进matchedHandlers,然后再将matchedHandlers放进handlerQueue队列
在处理完每层的委托事件后,将剩下的自身绑定事件再push进handlerQueue队列中
也就是说,handlerQueue的结构如下:
[ //委托事件 { elem:xx, handlers:[ {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 1}, {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 2}, ] }, //自身绑定事件 { elem:xxx, handlers:[ {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 3}, {type: "click", origType: "click", data: undefined, handler: ƒ, guid: 4}, ] }, ]
(5)回过头再往下看dispatch 源码,是两个while循环,举个例子来说明下:
这是A$("#A").on("click" ,function (event) { console.log(event,"A被点击了") }) $("#A").on("click" ,"#B",function (event) { console.log(event,"点击了B,即B委托A的click事件被点击了") })这是B
那么会
先循环并执行委托事件,
即handler=function (event) {console.log(event,"点击了B,即B委托A的click事件被点击了")},
再循环并执行目标元素自身绑定事件,
即handler=function (event) {console.log(event,"A被点击了")}
前提是冒泡不被阻止
最后,执行click事件的事件处理程序的关键代码如下:
handleObj.handler.apply( matched.elem, args )
(完)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/104663.html
摘要:源码源码行被点击了点击了,即委托的事件被点击了优先添加委托,再添加其他即委托在上的事件数量在下标为的位置插入委托事件解析可以看到,是优先添加委托事件,再添加自身事件,触发事件的时候也是按这个顺序。 showImg(https://segmentfault.com/img/remote/1460000019419722); 前言:请先回顾下我之前写的一篇文章:JavaScript之事件委...
摘要:十的触发机制被点击了元素本身绑定了一个事件,但是是原生事件,它是靠绑定来触发事件的。 showImg(https://segmentfault.com/img/remote/1460000019505402); 前言:最重要的还是最后的流程图,可以试着根据流程图手写实现$().on(),下篇文章会放出模拟实现的代码。 一、举例 这是A 这是C ...
摘要:专门为事件建立一个有问题,直接退出如果是一个事件处理对象,且有属性。参考源码分析事件体系结构解密事件核心绑定设计一解密事件核心委托设计二本文在上的源码地址,欢迎来。 欢迎来我的专栏查看系列文章。 通过前面一章对于 addEvent 库的介绍,它的兼容性超级棒,据说对于 IE4、5 都有很好的兼容性,这和 jQuery 的原理是一致的,而在 jQuery 中,有一个对象与其相对应,那就是...
阅读 2815·2023-04-25 18:58
阅读 957·2021-11-25 09:43
阅读 1198·2021-10-25 09:46
阅读 3478·2021-09-09 11:40
阅读 1555·2021-08-05 09:59
阅读 829·2019-08-29 15:07
阅读 930·2019-08-29 12:48
阅读 668·2019-08-29 11:19