摘要:一起源方法最終是用綁定事件的而方法正是等于二作用觸發綁定的事件的處理程序源碼源碼行即原生觸發事件的處理程序修正對象獲取事件的處理程序集合,結構如下從數據緩存中獲取事件處理集合即目標元素委托目標這段代碼壓根不會執行,因為全局搜索沒找到結構
一、起源
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 )
(完)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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 中,有一個對象與其相對應,那就是...
閱讀 2832·2023-04-25 18:58
閱讀 977·2021-11-25 09:43
閱讀 1210·2021-10-25 09:46
閱讀 3494·2021-09-09 11:40
閱讀 1679·2021-08-05 09:59
閱讀 869·2019-08-29 15:07
閱讀 956·2019-08-29 12:48
閱讀 695·2019-08-29 11:19