<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event fun</title> </head> <body> <div style="width:200px;height:600px;background-color: red;"> <div style="width:200px;height:500px;background-color: blue;"> <div style="width:200px;height:400px;background-color: green;"> <div id="a" style="width:200px;height:300px;background-color: black;"> <div style="width:200px;height:200px;background-color: yellow;"> </div> </div> </div> </div> </div> <script src="../../jquery-2.1.1.js"></script> <script> $(document).on('click',function(){}) $(document).on('click','#a',function(){}) $(document).on('click','div',{ name:'qq', age:'dd' },function(){ console.log(1);//5次 }) var doc = $(document) document.onclick = function(e){ console.log(1);//1次 } </script> </body> </html>
在這個有5層的html嵌套結構中,當在最內部的div上點擊一下。jQuery的事件代理會觸發5次。元素的會觸發1次。
上源碼:html
handlers: function( event, handlers ) {//新的事件對象 , 該類型的監聽對象數組。將當前元素的全部監聽事件排成一個序列,從底到頂,而後是普通事件 var i, matches, sel, handleObj, handlerQueue = [],//響應對象數組 delegateCount = handlers.delegateCount,//代理事件的數量 cur = event.target;//目標元素 // Find delegate handlers // Black-hole SVG <use> instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur !== this; cur = cur.parentNode || this ) {//從觸發了事件的目標元素,向上找,一直到代理的元素 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.disabled !== true || event.type !== "click" ) {//排除不支持click的元素 matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) {//判斷目標元素是否匹配selector的過濾 matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) {//匹配的話將響應對象入隊。 matches.push( handleObj ); } } if ( matches.length ) {//若是匹配,將元素和響應對象序列入數組 handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) {//綁定了普通的事件,入數組 。 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; },
接下來就是這個dispatch了,其實也很簡單,就幹了3樣事情。
一、檢查是否是特殊的事件,若是是優先使用特殊的處理函數
二、調用函數傳入jQuery事件對象。這就是爲何在處理函數內部的事件對象是jQuery的事件對象了。
三、檢查上函數的返回值是否是爲false,若是是就調用jQuery.Event對象的原型中的方法阻止冒泡和默認行爲。
上源碼:node
dispatch: function( event ) {//event爲原生事件對象 函數做用:主監聽函數 // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event );//建立事件對象 var i, j, ret, matched, handleObj,//ret返回值,matched放置匹配過的響應對象 handlerQueue = [],//待執行隊列。包括後代元素匹配的代理監聽對象數組 和 當前元素上綁定的普通監聽對象數組。 args = slice.call( arguments ),//把arguments轉換成真正的數組 handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],//當前事件類型對應的監聽對象數組 special = jQuery.event.special[ event.type ] || {};//獲取事件的修正對象 // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event;//存儲事件對象 event.delegateTarget = this;//代理對象 // 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 handlerQueue = jQuery.event.handlers.call( this, event, handlers );//肯定要執行的響應函數數組 // Run delegates first; they may want to stop propagation beneath us i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {//隊列中還有元素且沒有被阻止冒泡 event.currentTarget = matched.elem;//當前的元素 j = 0; 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.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {//沒有傳入命名空間,或者命名空間匹配 event.handleObj = handleObj;//複製 event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args );//優先調用修正事件處理函數,將返回值存在 if ( ret !== undefined ) {//返回值爲false是,阻止冒泡和默認行爲 if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) {//beforeUnload special.postDispatch.call( this, event ); } return event.result; },
off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) {//使用dispatched分發過的jquery處理函數對象,也就是事件正在被觸發 // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) {//types 是對象,用於一次性移除多個事件類型和過個監聽函數 // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) {//修正參數,selector爲false,或者只傳入兩個參數 // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) {//沒傳fn fn = returnFalse; } return this.each(function() {//調用remove刪除事件 jQuery.event.remove( this, types, fn, selector ); }); }, remove: function( elem, types, handler, selector, mappedTypes ) { var j, origCount, tmp, events, t, handleObj, special, handlers, type, namespaces, origType, elemData = data_priv.hasData( elem ) && data_priv.get( elem ); if ( !elemData || !(events = elemData.events) ) {//沒有關聯的緩存數據或者事件緩存對象 return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( rnotwhite ) || [ "" ];//轉換成數組 t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) {//若是沒有指定事件類型,則移除元素全部事件或命名空間中全部事件(有給定命名空間的狀況) for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {};//得到修正對象(若是有) type = ( selector ? special.delegateType : special.bindType ) || type;//若是有傳selector則修正爲代理事件,不然優先考慮修正爲更好的事件 handlers = events[ type ] || []; tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );//用於檢測已綁定事件和types的命名空間是否同樣 // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) &&//mappedTypes不爲真時比較傳入類型和監聽對象的原始事件類型 ( !handler || handler.guid === handleObj.guid ) &&//沒有指定監聽函數 或 指定監聽函數與監聽對象具備同樣的id ( !tmp || tmp.test( handleObj.namespace ) ) &&//沒有指定命名空間或者監聽對象的命名空間具備指定的命名空間 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {//沒傳入selector 或 有傳入可是與監聽對象的相等 或 爲"**"(全部)是監聽對象有selector handlers.splice( j, 1 );//從監聽對象數組中刪除 if ( handleObj.selector ) {//若是刪除了的是代理事件 則修正dele gateCount 以便下一次插入代理事件的正確 handlers.delegateCount--; } if ( special.remove ) {//有對應的修正方法remove,則調用 special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) {//某類型事件監聽對象數組被清空,刪除主監聽函數 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {優先調用teardown移除主監聽函數 jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ];//從總監聽對象數組中刪除該類型的監聽對象數組 } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) {//總監聽對象數組爲空,說明該元素上的全部事件都被移除 delete elemData.handle;//移除主監聽函數儲存數據的對象 data_priv.remove( elem, "events" );//移除緩存 } },