« fladdictパッケージ製作日記(1) | main | 近況報告 »

2005年06月03日

イベント・ドリブンなJavaScriptのやり方

Flashでに学ぶ非同期通信のノウハウのって感じのお話。JavaScriptでオブザーバパターンを用いて、イベントドリブンなコード(addEventListener等)をバリバリ書く方法です。

というかFlashで言うところの、mx.events.EventDispatcherクラスの自分なりの移植なわけですけど。

このEventDispatcherクラスを使うと、W3CのDOM3にそれなりに対応した、イベントモデルを自作のJavaScriptのオブジェクトに実装できます。これで素晴らしきイベントドリブンの世界が楽しめます。

function EventDispatcher(){}


/**
EventDispatcher.initialize( obj )

オブジェクトobjに、W3C DOM3互換のイベント通知機能を追加します。
これによりobjは、onLoad等のイベントを発行することができます。
オブジェクトobjはこれにより、以下のプロパティと関数を装備します。
obj.addEventListener( イベント名, リスナーオブジェ);
obj.removeEventListener( イベント名, リスナーオブジェクト);
obj.dispatchEvent( Eventオブジェクト);

@param obj オブジェクト
*/


EventDispatcher.initialize = function(obj){
//MIX IN following property and function int obj
//be careful about Name Scape Corrision just for incase
obj.__ed_eventContainer = new Object();
obj.addEventListener = this._addEventListener;
obj.removeEventListener = this._removeEventListener;
obj.dispatchEvent = this._dispatchEvent;
}

/*
CAUTION INTERNAL OBJECT do not call it directory from EventDispatcher
refered from targetObject.addEventListener()
*/

EventDispatcher._addEventListener = function(eventName, object){
//CAUTION
//third param is not implemented yet

//CAUTION:
//scope of this object is always "TARGET" OBJECT, not EventDispatcher
if( this.__ed_eventContainer[eventName]==null){
this.__ed_eventContainer[eventName]=new Array();
}
this.removeEventListener(eventName, object);
this.__ed_eventContainer[eventName].push(object); //register object
}


/*
CAUTION INTERNAL OBJECT do not call it directory from EventDispatcher

refered from targetObject.removeEventListener()
*/

EventDispatcher._removeEventListener = function(eventName, object){
//CAUTION:
//scope of this object is always "TARGET" OBJECT, not EventDispatcher
var listener_ar = this.__ed_eventContainer[eventName];
if(listener_ar == undefined) return;

var imax = listener_ar.length;
for(var i=0; i var listener = listener_ar[i];
if(listener==object){
listener_ar.splice(i,1);
return;
}
}
}


/*
CAUTION INTERNAL OBJECT do not call it directory from EventDispatcher
refered from targetObject.dispatchEvent()
*/

EventDispatcher._dispatchEvent = function(eventObj){
//CAUTION:
//scope of this object is always "TARGET" OBJECT,not EventDispatcher
if(eventObj.target==null) eventObj.target = this;

var eventName = eventObj.type;
if( this.__ed_eventContainer[eventName]==null) return;

var imax = this.__ed_eventContainer[eventName].length;
for(var i=0; i var listener = this.__ed_eventContainer[eventName][i];
if(typeof(listener)=="object"){
listener[eventName].apply(listener, new Array(eventObj));
return;
}else{
listener(eventObj);
return;
}
}
}

厳密にテストしたわけじゃないけど、ちゃんと動いてると思いますたぶん。

使い方としては

EventDispatcher.initialize(hogeObject)

と一行、リスナー通知機能を実装したいオブジェクトをEventDispatcherのinitializeメソッドに渡してやるだけです。
こうするとhogeObjectに以下の3つの関数が実装されます。
addEventListener(イベント名, リスナー);
removeEventListener(イベント名, リスナー);
dispatchEvent(イベントオブジェクト);


addEventListener(イベント名, リスナー);
普通に、JavaScriptのaddEventListener互換の機能です。第三引数のみ未実装ですが。
例えば、hogeObj.addEventListener("load", hogehogeObje)とすると、hogehogeObjにloadイベントが通知されるようになります。


removeEventListener(イベント名, リスナー);
イベント登録の解除です。リスナー(上記例ではhogehogeObj)を消去するときにはちゃんと呼び出さないと、メモリにゴミが溜まる可能性があるので注意。


dispatchEvent(イベントオブジェクト);
イベントを、リスナーに通知する為のメソッドです。(上記例ではhogeObj内で状態が遷移したときに、hogeObjがこれを実行することで、リスナーにイベントを通知します)。
引数のイベントオブジェクトというのは、リスナーに対して渡してやりたい情報をオブジェクト化したもの。通常は
dispatchEvent({type:イベント名, target:this})
と指定しておく。

通常、EventDispatcher.initializeはリスナ通知機能を持ちたいオブジェクトのコンストラクタで行うのがよいです。


コードにするとこんな感じ。
イベント通知機能を実装するhogeクラスのコード。ダイレクトに書いたので細かいチョンボがあるかも・・・

//hogeオブジェクト
function hoge(){
//hogeインスタンスに、イベント通知機能を実装
EventDispatcher.initialize(this);
}

hoge.prototype.doSomething = function(){
//doSomethingを実行したらイベントを通知
this.dispatchEvent({type:"onSomething", target:this});
}

で、実行コードはこんな感じ

var h = new hoge(); // イベント機能つきhogeインスタンス作成。

var listener = new Object();
//イベントハンドラ
listener.onSomething = function(e){
alert("event受信 " + e.type);
}

h.addEventListener("onSomething", listener);

h.doSomething(); //doSomething実行のラストでイベントが発行される。

プロトタイプベース言語は、こういう風にオブジェクトに動的に機能拡張できるから好きです。Ajaxに限らず、JavaScriptでOOPやるならMIXINによるパターンを勉強すると便利。

投稿者 Taka : 2005年06月03日 23:13

book

dotfla.gif

bookmark

はてなブックマークに追加

del.icio.usに追加

trackbacks

this entry's trackback URL:
http://www.fladdict.net/cgi-bin/mt3/mt-tb.cgi/197

このリストは、次のエントリーを参照しています: イベント・ドリブンなJavaScriptのやり方:

» phentermine from phentermine
[read more]

トラックバック時刻: 2006年03月07日 21:06

» family health insurance plans from family health insurance plans
family health insurance plans famil... [read more]

トラックバック時刻: 2006年03月19日 01:28

comment

ちょうどFlashのEventDispatcherを勉強しているところなんですが、同様にECMA-262のスクリプトってことでJavascriptにも同じものが移植できたらいいかなぁとおもっていたら、すでにこちらにあって驚きました。

Javascript←→ActionScriptの相互乗り換えが楽になりそうです。

by Takachin : 2005年06月06日 20:26

イベントドリブン便利っすよー、やっぱ。

Flash-JSの技術交換ネタはしばらく続けていきまっすので、まったりとお楽しみを。

by Taka : 2005年06月07日 11:30

コメントしてください




保存しますか?