所謂的事件機制,就是用於實現和事件相關的功能的函數,一般我們操作事件需3個功能:
綁定
取消綁定
執行
比如
elem.on('click', fn);
所有js架構都提供事件函數,因為:
瀏覽器內建的事件不相容。
目前常用的事件實現有:
[Ext] elem.on elem.un elem.fireEvent (addEventListener 等簡寫)
[jQuery] elem.bind elem.unbind elem.trigger
[mootools] elem.addEvent elem.removeEvent elem.fireEvent
我使用的是:
elem.on elem.un elem.trigger - 因為最短
(PS,使用 triggerListener 是錯誤的, 應該 dispatchListener)
jQuery中,將事件名作為函數方便使用。
事件內部如何??
也就是說當我們執行 on('click', fn); 做什麼事?
最簡單也是不可採用的方法是mootools的做法:
對不同的瀏覽器, IE 使用 attchEvent , 其它使用 addEventListenr
這樣感覺是不錯,但有2個問題:
當事件有幾百個後,效率很低。(不常見)
當 on('click', fA) , on('click', fB)
這時如果click, IE先運行 fB 然後 fA 其它則正常地運行 fA和fB 。(容易造成問題)
要說好的事件機制,當是jQuery的實現。(但效率稍低)
首先,jQuery內部有世界緩衝數組。這個數組可用 $().data('event') 返回。這個資料結構為:
event = {
click: [fA, fB]
dblclick: [fC]
};
當瀏覽器觸發 click 後,遍曆 event['click'] 全部函數和執行。
我最後使用以下方式實現。
基本和jQuery的一致, 但event資料結構為:
event = {
click: fn_click, // 系統click 後執行 fn_click , fn_click 執行裡面的所有函數。
...
}
fn_click = {
handlers: [fA, fB]
target: elem
}
然後是 事件對象的封裝。
所謂的事件對象,就是綁定函數的參數 e 。
(熟悉 .net 的同學肯能更喜歡叫它 EventArgs )
因為不同瀏覽器這個對象不同,所以封裝是一定的。
目前全部架構都使用自訂事件對象的方式,把原事件的各值拷到自訂的對象。
但這樣有缺點,就是很多值也許用不到,無故計算浪費效率,(事件是常執行的,這效率不可忽略)
Ext則使用函數方式,減小沒用的拷貝。如擷取螢幕座標,使用
e.getX() 這有點不美觀。但將就了。
我經過多次的考慮,決定不自訂事件對象。直接用內建的,
這樣有個缺點: Firefox下無法設定系統 getter 屬性。
也就是說:
e.target = e.target.nodeType == 3 ? e.target.parentNode : e.target; // 標準瀏覽器肯能 target 是文本節點。 IE的 target則是 元素。
在Firefox是錯的。 因為 target是唯讀。
接下來是類比事件觸發,
在 jQuery 中,很辛苦的用代碼類比了支援冒泡的事件。 雖然在IE有完美的 fireEvent ,標準瀏覽器也有 dispatchListener 函數。
不過如果自己類比,可以避免瀏覽器不同,實現統一。