1. 程式人生 > >前端事件系統(四)

前端事件系統(四)

編前語:事件是前端之中,非常重要的一個部分。其作用在於對於使用者的各種行為進行相應。前端事件系統(一)中主要介紹事件的繫結方式和事件處理;前端事件系統(二)中主要介紹JQuery事件繫結;前端事件系統(三)中主要介紹on與event.add方法。

事件派發

首先來看看jQuery.event的dispatch方法

JavaScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 dispatch:function(event){// 對event物件進行修正event=jQuery.event.fix(event);vari,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 typeif(special.postDispatch){special.postDispatch.call(this,event);}returnevent.result;},

通讀整段程式碼,歸納一下,jQuery.event.dispatch是做了一下幾項處理的

  1. 對event物件進行修復
  2. 讀取事件控制代碼
  3. 取得事件佇列並對其進行處理

先看第一點

對event物件的修復,這一步得到event物件,是jQuery的event物件,而非原生的物件。這裡jQuery將原本只讀的物件,變為了一個可讀可寫的物件,這樣就可以對其進行隨意操作了。不過對於event物件修復,我打算現將其放到下一章與事件的修復一起進行講解,因此這裡只需要知道,這裡是返回的jQuery的event物件就行了。

第二點也不再多說,就是讀取了事件的快取

那麼來到第三點,也是事件分發的另外一個重點

事件佇列的獲取與處理。

在之前版本的jQuery中,佇列的生成與處理,都是放在了dispatch中進行,不過如今佇列已經交由jQuery.event.handlers生成並返回,那麼我們首先來看下獲取到了handlerQueue到底是什麼,即也就是對於jQuery.event.handlers來進行閱讀。

jQuery.event.handlers

JavaScript
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 handlers:function(event,handlers){vari,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];// 獲取selectorsel=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)});}returnhandlerQueue;}

這一部分,整體看來比較複雜,我們來理一下一下。

這部分先對事件代理做了判斷並進行了處理,採用match來對符合條件的事件控制代碼做一個篩選,並將所有符合條件的事件控制代碼,按從深及淺的順序,一一放入了事件佇列之中。

然後,在處理完成了事件代理之後,採用delegateCount區分事件代理以及直接繫結,再將直接繫結的事件控制代碼,放入事件佇列之中,生成了最終的事件佇列。這樣,最終得到的,就是一個委託層次越深,便越會提前執行的事件佇列。

因此,事件委託,在這一步就已經完成了。同時,因為jQuery的事件處理機制,是這樣一個佇列的形式,因此,之前在第一章末尾所提及的,對於執行順序的問題,這裡也很好的解決了。

再回來看dispatch對於事件佇列的處理

那麼最後,我們來看dispatch中如何對於這個事件佇列進行的處理。

JavaScript
123456789101112131415161718192021222324252627 // 對於事件佇列的處理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以及stopPropagationif(ret!==undefined){if((event.result=ret)===false){event.preventDefault();event.stopPropagation();}}}}}

這一部對於事件佇列進行了有序的執行(由深及淺再到本身),然後,在這個過程中,通過已經修正過的jQuery事件物件,動態的改變event物件的屬性,在執行事件控制代碼。同時,也對return false後,直接呼叫event.preventDefault(),與event.stopPropagation()進行了處理。

總結:

那麼,到目前,對於事件繫結這一塊,除了對於事件的修復部分,其他的部分都已經閱讀完畢。我們到最後再來理一下整個的過程。

  1. 首先,繫結時,採用了on方法,在這個過程中,on對於我們繫結時候,所傳進來的引數,一一進行了處理,並最終將其傳入jQuery.event.add中來執行。
  2. event.add部分,引用了dispatch方法進行了事件分發,並將事件名與事件控制代碼進行了填充
  3. dispatch部分,對於event物件進行修復,讀取事件控制代碼,同時取得事件佇列並對其進行處理,並執行回撥。

那麼下一章,將對jQuery事件物件的修復,以及事件的修復,進行一個講解。