這節 我們將學習dojo/on ,dojo是如何讓連接dom 事件變得簡單的,我們也會探索dojo 的訂閱/發佈 框架dojo/topic
.
你的太多的javascript 代碼是爲事件而準備的,響應或者生成新的事件。這將意味着創建快速反映和交互的web應用的關鍵就是創建高效的事件連接。
事件連接允許你的應用響應用戶的交互
等待動作事件的發生,dojo 的主要的dom 事件處理機制是dojo/on,
我們來學習此模塊。
你也許會問
Dom
是否已經提供了一個事件處理機制,去註冊事件處理者,是的的確提供了,但不是所有
的瀏覽器都遵循
dom
標準。
在主流的瀏覽器中對事件的處理有三種方式
(addEventListener, attachEvent, and DOM0)
在一個瀏覽器中使用兩個不同的事件對象會將註冊的方法的處理器置於隨機的順序而引起內存泄露,你將會面臨一個災難。
幸運的是他們將解決不同DOM api 差異性和防止內存泄露放在了一個API 裏面:dojo/on
。
我們來看下面的標記:
<button id="myButton">Click me!</button>
<div id="myDiv">Hover over me!</div>
設想一下當你按下按鈕時,你想要div 的顏色變成 藍色的,當你離開的時候想要變成紅色的。
當你移上鼠標時變成白色 dojo/on
實現起來非常的簡單。
require(["dojo/on", "dojo/dom-style", "dojo/mouse", "dojo/domReady!"],
function(on, domStyle, mouse) {
var myButton = document.getElementById("myButton"),
myDiv = document.getElementById("myDiv");
on(myButton, "click", function(evt){
domStyle.set(myDiv, "backgroundColor", "blue");
});
on(myDiv, mouse.enter, function(evt){
domStyle.set(myDiv, "backgroundColor", "red");
});
on(myDiv, mouse.leave, function(evt){
domStyle.set(myDiv, "backgroundColor", "");
});
});
注意:
dojo/mouse
同樣需要
dojo/on
不是原生的支持
mouseenter
和
mouseleave
事件,
dojo/mouse
添加了這樣的支持。
這個例闡述了一般的模式
:on(
element,
event name,
handler)
還可以使用令有一個模式:在當前元素上加一個事件,連接的處理器。
這種模式適用於所有的
window,document, node, form, mouse, and keyboard 事件。
On 方法不但規範了註冊事件的API,而且規範化了事件處理器是如何被調用的。
1. 事件處理器按照註冊的順序來調用,
2.
他們總是把一個事件對象當作他們的第一個參數。
3.
事件對象擁有一個
.target
(目標)屬性
一個
stopPropagation
(阻止傳播)方法和一個
preventDefault
(阻止默認值)方法
就像
DOM的API Dojo 提供一個方法刪除事件處理器。handle.remove
。
on
方法的返回值是一個擁有
remove
方法的簡單對象,調用此方法將刪除事件監聽。例如你想要一個只執行一次
的方法你可以按照下面做。
var handle = on(myButton, "click", function(evt){
// Remove this event using the handle
handle.remove();
// Do other stuff here that you only want to happen one time
alert("This alert will only happen one time.");
});
順便說一下:
dojo/on
包含了一個便利的方法處理這些一次性的事件
on.once
.他接收和on一樣的參數。
一旦結束解除將刪除事件處理器。
最後一點要記住的是:on 將執行在第一個參數傳入的節點環境的事件處理器。
一個例外是:當on 使用委派事件。
然而你可以使用lang.hitch
(在
dojo/_base/lang
模塊中)指定運行處理器的上下文。
使用對象方法時
Hitching是非常的有用的。
require(["dojo/on", "dojo/_base/lang", "dojo/domReady!"],
function(on, lang) {
var myScopedButton1 = document.getElementById("myScopedButton1"),
myScopedButton2 = document.getElementById("myScopedButton2"),
myObject = {
id: "myObject",
onClick: function(evt){
alert("The scope of this handler is " + this.id);
}
};
// This will alert "myScopedButton1"
on(myScopedButton1, "click", myObject.onClick);
// This will alert "myObject" rather than "myScopedButton2"
on(myScopedButton2, "click", lang.hitch(myObject, "onClick"));
});
節點列表事件
節點列表提供了一個方法註冊事件到多個節點。
On 方法 ,此方法使用了同dojo/on
不使用第一個參數一樣的格式(因爲在節點列表中的節點就是你要連接事件的節點)
On
方法包含了
dojo/query
所以你不需要明確的指定
dojo/on
(當使用
NodeList.on
.)的時候。
來看一個更高級一點的例子。
注意:
NodeList.on
返回一個存放
on
事件處理器的一個數組。這些處理器可以在以後刪除。
數組也包含一個便利的頂級刪除方法,一次性上拿出所有的事件監聽器。
事件委託
如上所述,節點列表的On 方法使得將相同的處理器勾連到多個Dom的相同的事件上。
dojo/on 還有一個更加高效的方法來實現此種效果,那就是事件委託。
事件委託的背後機制是:取代爲每個事件連接一個監聽器(在每個你感興趣的節點上),在一個更高級別上,你附加一個事件到一個節點上。他將查看他所捕獲事件的目標,去查看他是否是真正想要的真實節點的冒泡,如果是處理器的行爲將被執行。
使用的格式是:on(parent element, "selector:event name",handler).
爲了更好的說明,請看下面的例子:
<div id="parentDiv">
<button id="button1" class="clickMe">Click me</button>
<button id="button2" class="clickMe">Click me also</button>
<button id="button3" class="clickMe">Click me too</button>
<button id="button4" class="clickMe">Please click me</button>
</div>
<script>
require(["dojo/on", "dojo/query","dojo/domReady!"],
function(on){
varmyObject = {
id:"myObject",
onClick:function(evt){
alert("Thescope of this handler is " + this.id);
}
};
var div =document.getElementById("parentDiv");
on(div,".clickMe:click", myObject.onClick);
});
</script>
注意: 雖然我們不直接的使用dojo/query,但此模塊仍然是需要的。
這是因爲:dojo/on 需要一個 dojo/query 暴露的選擇器引擎用來匹配事件委託所使用的選擇器。
他沒有被自動的加在dojo/on 用來減少他出現的次數,避免爲了一個不經常使用的特性而增加開發者的負擔。
運行上面的展示例子注意 this 依然指向我們真正感興趣的節點。
而不是 parentDiv 節點,當使用委託時一個重要的區別:this不再指向傳入的第一個參數的節點,而是指向匹配選擇器的節點。
一旦你想要知道他時,這確實是非常有用的。
Publish/Subscribe(發佈/訂閱)
在此之前,以上的所有例子都使用一個已存在的對象作爲事件的發生器(你註冊的等待事件的發生)。
如果你沒有一個節點的引用,或者並不知道對象是否已經創建。這就是dojo 的發佈/訂閱框架引入的原因。
通過 dojo/topic模塊,Pub/sub允許你爲一個主題註冊一個處理器,(主題是一個事件的別名,此事件是多源的,以字符串形式描述)當配發布的時候主題將被調用。
我們設想一下,在我們開發的應用中,我們需要一些按鈕,來彈出動作的用戶,我們想要一次性的將彈出寫完,我們也不想創建一個包裝對象,同過按鈕來註冊此小程序,Pub/sub能夠應用在此場景中。
<button id="alertButton">Alert the user</button>
<button id="createAlert">Create another alert button</button>
require(["dojo/on", "dojo/topic", "dojo/dom-construct", "dojo/domReady!"],
function(on, topic, domConstruct) {
var alertButton = document.getElementById("alertButton"),
createAlert = document.getElementById("createAlert");
on(alertButton, "click", function() {
// When this button is clicked,
// publish to the "alertUser" topic
topic.publish("alertUser", "I am alerting you.");
});
on(createAlert, "click", function(evt){
// Create another button
var anotherButton = domConstruct.create("button", {
innerHTML: "Another alert button"
}, createAlert, "after");
// When the other button is clicked,
// publish to the "alertUser" topic
on(anotherButton, "click", function(evt){
topic.publish("alertUser", "I am also alerting you.");
});
});
// Register the alerting routine with the "alertUser" topic.
topic.subscribe("alertUser", function(text){
alert(text);
});
});
</script>
這個事件模式的一大優勢是:我們的彈出程序可以在一個單元測試中測試,而不需要創建任何的Dom對象,程序同事件的發生器進行了解耦。
如果你想停止接受主題的通知。topic.subscribe 返回一個包含 remove 方法的對象,能夠刪除各自的處理器。
注意:不像 dojo.publish, topic.publish不期望傳入的發佈參數放在數組中,例如
topic.publish("someTopic","foo", "bar")和dojo.publish("someTopic",["foo", "bar"])是等價的。
總結:
Dojo的事件系統是相當的強大、非常的易用。On 方法解決了dom 事件的跨瀏覽器問題。Dojo 的pub/sub 框架,dojo/topic
讓開發者能夠容易的將事件處理器與事件發生器解耦,花費一些時間來熟悉這些工具,他們將會是你創建你web應用的寶貴財富。