編前語:事件是前端之中,非常重要的一個部分。其作用在於對於使用者的各種行為進行相應。前端事件系統(一)中主要介紹事件的繫結方式和事件處理;前端事件系統(二)中主要介紹JQuery事件繫結;前端事件系統(三)中主要介紹on與event.add方法。
事件派發
首先來看看jQuery.event的dispatch方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
dispatch: function( event ) { // 對event物件進行修正 event = jQuery.event.fix( event ); var i, j, ret, matched, handleObj, handlerQueue = [], args = slice.call( arguments ), // 讀取事件快取 handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // 將引數一重置為jQuery的事件物件 args[0] = event; // 新增delegate屬性,用於事件代理 event.delegateTarget = this; if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // 取得事件佇列 handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // 對於事件佇列的處理 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 ) { 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 ); } return event.result; }, |
通讀整段程式碼,歸納一下,jQuery.event.dispatch是做了一下幾項處理的
- 對event物件進行修復
- 讀取事件控制程式碼
- 取得事件佇列並對其進行處理
先看第一點
對event物件的修復,這一步得到event物件,是jQuery的event物件,而非原生的物件。這裡jQuery將原本只讀的物件,變為了一個可讀可寫的物件,這樣就可以對其進行隨意操作了。不過對於event物件修復,我打算現將其放到下一章與事件的修復一起進行講解,因此這裡只需要知道,這裡是返回的jQuery的event物件就行了。
第二點也不再多說,就是讀取了事件的快取
那麼來到第三點,也是事件分發的另外一個重點
事件佇列的獲取與處理。
在之前版本的jQuery中,佇列的生成與處理,都是放在了dispatch中進行,不過如今佇列已經交由jQuery.event.handlers生成並返回,那麼我們首先來看下獲取到了handlerQueue到底是什麼,即也就是對於jQuery.event.handlers來進行閱讀。
jQuery.event.handlers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
handlers: function( event, handlers ) { var i, matches, sel, handleObj, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // 判斷是否是事件代理、與是否是滑鼠左鍵的點選事件 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) // 不對disabled的元素進行click的處理 if ( cur.disabled !== true || event.type !== "click" ) { // 收集符合條件的事件控制程式碼 matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // 獲取selector sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) { // 匹配事件控制程式碼 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 }); } } } } // 在事件佇列中,增加其他直接繫結的事件控制程式碼 if ( delegateCount < handlers.length ) { handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; } |
這一部分,整體看來比較複雜,我們來理一下一下。
這部分先對事件代理做了判斷並進行了處理,採用match來對符合條件的事件控制程式碼做一個篩選,並將所有符合條件的事件控制程式碼,按從深及淺的順序,一一放入了事件佇列之中。
然後,在處理完成了事件代理之後,採用delegateCount區分事件代理以及直接繫結,再將直接繫結的事件控制程式碼,放入事件佇列之中,生成了最終的事件佇列。這樣,最終得到的,就是一個委託層次越深,便越會提前執行的事件佇列。
因此,事件委託,在這一步就已經完成了。同時,因為jQuery的事件處理機制,是這樣一個佇列的形式,因此,之前在第一章末尾所提及的,對於執行順序的問題,這裡也很好的解決了。
再回來看dispatch對於事件佇列的處理
那麼最後,我們來看dispatch中如何對於這個事件佇列進行的處理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// 對於事件佇列的處理 i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { 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 ); //直接return false,即可event.preventDefault以及stopPropagation if ( ret !== undefined ) { if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } |
這一部對於事件佇列進行了有序的執行(由深及淺再到本身),然後,在這個過程中,通過已經修正過的jQuery事件物件,動態的改變event物件的屬性,在執行事件控制程式碼。同時,也對return false後,直接呼叫event.preventDefault(),與event.stopPropagation()進行了處理。
總結:
那麼,到目前,對於事件繫結這一塊,除了對於事件的修復部分,其他的部分都已經閱讀完畢。我們到最後再來理一下整個的過程。
- 首先,繫結時,採用了on方法,在這個過程中,on對於我們繫結時候,所傳進來的引數,一一進行了處理,並最終將其傳入jQuery.event.add中來執行。
- event.add部分,引用了dispatch方法進行了事件分發,並將事件名與事件控制程式碼進行了填充
- dispatch部分,對於event物件進行修復,讀取事件控制程式碼,同時取得事件佇列並對其進行處理,並執行回撥。
那麼下一章,將對jQuery事件物件的修復,以及事件的修復,進行一個講解。