天天看點

dojo事件驅動程式設計之事件綁定

什麼是事件驅動?

  事件驅動程式設計是以事件為第一驅動的程式設計模型,子產品被動等待通知(notification),行為取決于外來的突發事件,是事件驅動的,符合事件驅動式程式設計(event-driven programming,簡稱edp)的模式。

  何謂事件?通俗地說,它是已經發生的某種令人關注的事情。在軟體中,它一般表現為一個程式的某些資訊狀态上的變化。基于事件驅動的系統一般提供兩類的内建事件(built-in event):一類是底層事件(low-level event)或稱原生事件(native event),在使用者圖形界面(gui)系統中這類事件直接由滑鼠、鍵盤等硬體裝置觸發;一類是語義事件(semantic event),一般代表使用者的行為邏輯,是若幹底層事件的組合。比如滑鼠拖放(drag-and-drop)多表示移動被拖放的對象,由滑鼠按下、滑鼠移動和滑鼠釋放三個底層事件組成。

  還有一類使用者自定義事件(user-defined event)。它們可以是在原有的内建事件的基礎上進行的包裝,也可以是純粹的虛拟事件(virtual event)。除此之外,程式設計者不但能定義事件,還能産生事件。雖然大部分事件是由外界激發的自然事件(natural event),但有時程式員需要主動激發一些事件,比如模拟使用者滑鼠點選或鍵盤輸入等,這類事件被稱為合成事件(synthetic event)。這些都進一步豐富完善了事件體系和事件機制,使得事件驅動式程式設計更具滲透性。

  

dojo事件驅動程式設計之事件綁定

  上圖為一個典型的事件驅動式模型。事件處理器事先在關注的事件源上注冊,後者不定期地發表事件對象,經過事件管理器的轉化(translate)、合并(coalesce)、排隊(enqueue)、分派(dispatch)等集中處理後,事件處理器接收到事件并對其進行相應處理。通過事件機制,事件源與事件處理器之間建立了松耦合的多對多關系:一個事件源可以有多個處理器,一個處理器可以監聽多個事件源。再換個角度,把事件處理器視為服務方,事件源視為客戶方,便是一個client-server模式。每個服務方與其客戶方之間的會話(session)是異步的,即在處理完一個客戶的請求後不必等待下一請求,随時可切換(switch)到對其他客戶的服務。

  在web環境中事件源由dom充當,事件管理器對于web開發者來說是透明的,由浏覽器内部管理,事件處理器便是我們綁定在dom事件中的回調函數。

  web事件處理流程

  dom2.0模型将事件處理流程分為三個階段:一、事件捕獲階段,二、事件目标階段,三、事件起泡階段。如圖:

dojo事件驅動程式設計之事件綁定

  事件捕獲:當某個元素觸發某個事件(如onclick),頂層對象document就會發出一個事件流,随着dom樹的節點向目标元素節點流去,直到到達事件真正發生的目标元素。在這個過程中,事件相應的監聽函數是不會被觸發的。

  事件目标:當到達目标元素之後,執行目标元素該事件相應的處理函數。如果沒有綁定監聽函數,那就不執行。

  事件起泡:從目标元素開始,往頂層元素傳播。途中如果有節點綁定了相應的事件處理函數,這些函數都會被一次觸發。如果想阻止事件起泡,可以使用e.stoppropagation()(firefox)或者e.cancelbubble=true(ie)來組織事件的冒泡傳播。

  然而在此末法時代,浏覽器兩大派别對于事件方面的處理,常常讓前端程式員大傷腦筋,是以任何前端庫首先要對事件機制進行統一。

  dojo中的事件綁定

  dojo事件體系能夠幫我們解決哪些問題?

解決浏覽器相容性問題:觸發順序、this關鍵字、規範化的事件對象(屬性、方法)

可以在一個事件類型上添加多個事件處理函數,可以一次添加多個事件類型的事件處理函數

統一了事件的封裝、綁定、執行、銷毀機制

支援自定義事件

擴充組合事件

   dojo中處理浏覽器事件的代碼位于dojo/on子產品中,在官網中可以檢視該函數的簽名:

dojo事件驅動程式設計之事件綁定

  其中type可以是一個事件名稱如:“click”

亦可以是由逗号分隔的多個事件名組成的字元串,如:"dblclick,click"

亦可以是由冒号分隔"selector:eventtype"格式進行事件委托使用的字元串,如:".myclass:click"

亦可以是一個函數,如:touch.press、on.selector()

檢視一下on函數的源碼

  如果target自己擁有on方法則調用target自己的on方法,如_widgetbase類有自己的on方法,再比如jquery對象也會有自己的on方法,此處this關鍵字指向window。

  下面來看一下事件解析的過程:

如果type是方法,則交給type自身去處理;比如touch.press 、on.selector

多事件的處理;事件可能是通過逗号鍵分隔的字元串,是以将其變成字元串數組

對于事件數組依次調用on.parse

添加事件監聽器

 接着看一下事件監聽器的處理過程:

處理事件委托,dojo中事件委托的書寫格式為:“selector:eventtype”,直接交給on.selector處理

對與touchevent事件的處理,具體分析以後再說

對于stopimmediatepropagation的修正

支援addeventlistener的浏覽器,使用浏覽器自帶的接口進行處理

對于不支援addeventlistener的浏覽器進行進入fixattach函數

 對于上面的分析我們可以得出幾個結論:

對于沒有特殊eventtype和普通事件都用addeventlistener來添加事件了。

而特殊eventtype,則用了另一種方式來添加事件(fixattach)。

對于事件委托交給了on.selector處理

  來詳細的看一下fixattach:

  1、修正事件監聽器,該過程傳回一個閉包,閉包中對event對象進行修正,主要有一下幾方面:

target

currenttarget

relatedtarget

stoppropagation

preventdefault

event的坐标位置相容放到了dom-geometry的normalizeevent中處理

keycode與charcode的處理

        調用on中傳入的事件監聽器,如果監聽器中掉用過stopimmediatepropagation緩存lastevent,供以後使用

  2、對于低版本浏覽器防止在frames和為連結到dom樹中元素添加事件時引起的記憶體洩露,這裡自定義一個event對象,将所有的事件監聽器作為屬性添加到這個event對象上。

  3、不在2條件中的情況使用aspect.after構造一個函數鍊來存放事件監聽器,這就保證了監聽器的調用順序與添加順序一緻。

  接下來我們看一下委托的處理:

dojo事件驅動程式設計之事件綁定

  為document綁定click事件,click事件出發後,判斷event.target是否滿足選擇符“button.myclass”,若滿足則執行clickhandler。為什麼要判斷event.target是否滿足選擇條件,document下可能有a、也可能有span,我們隻需要将a的click委托給document,是以要判斷是否滿足選擇條件。委托過程的處理主要有兩個函數來解決:on.selector、on.matches.

  on.selector中傳回一個匿名函數,匿名函數中做了幾件事:

處理matchestarget在matches方法中使用

如果eventtype含有bubble方法進行特殊處理

其他普通情況,為代理元素綁定事件回調

dojo事件驅動程式設計之事件綁定

  紅框部分就是判斷event.target是否比對選擇符,如果比對則觸發事件回調clickhandler.

  on.matches中做了以下幾件事:

擷取有效的matchestarget,matchestarget是一個擁有matches方法的對象,預設取dojo.query

對textnode做處理

檢查event.target的祖先元素是否滿足比對條件

 對比dojo與jquery的事件處理過程,可以發現jquery在事件存儲上更上一籌:

  dojo直接綁定到dom元素上,jquery并沒有将事件處理函數直接綁定到dom元素上,而是通過.data存儲在緩存.cahce上。

   聲明綁定的時候:

首先為dom元素配置設定一個唯一id,綁定的事件存儲在

.cahce[唯一id][.expand ][ 'events' ]上,而events是個鍵-值映射對象,鍵就是事件類型,對應的值就是由事件處理函數組成的數組,最後在dom元素上綁定(addeventlistener/attachevent)一個事件處理函數eventhandle,這個過程由 jquery.event.add 實作。

  執行綁定的時候:

當事件觸發時eventhandle被執行,eventhandle再去$.cache中尋找曾經綁定的事件處理函數并執行,這個過程由 jquery.event. trigger 和 jquery.event.handle實作。

事件的銷毀則由jquery.event.remove 實作,remove對緩存$.cahce中存儲的事件數組進行銷毀,當緩存中的事件全部銷毀時,調用removeeventlistener/ detachevent銷毀綁定在dom元素上的事件處理函數eventhandle。

  如果您覺得這篇文章對您有幫助,請不吝點選一下右下方的推薦,謝謝!

  參考文章:

繼續閱讀