サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

EaselJS 0.8.1: イベントリスナーを扱うEventDispatcherクラス

ID: FN1302001 Technique: HTML5 and JavaScript Library: EaselJS 0.8.1

EventDispatcherクラス
継承 EventDispatcher → Object
ディレクトリ createjs/events
EventDispatcher()コンストラクタ
文法 EventDispatcher()
概要 EventDispatcherインスタンスをつくる。
引数 なし。
addEventListener()メソッド
文法 addEventListener(type, listener, useCapture)
概要 第1引数のイベントに第2引数のリスナーを加える。
引数

type − イベントの名前(種類)を定める文字列。

listener − イベントが起こったときに呼出す関数またはオブジェクト。リスナーとしてオブジェクトを加えると、そのhandleEvent()メソッドが呼出される。

useCapture − バブリングするイベントでtrueを渡すと、リスナーはキャプチャフェーズでイベントを捉える。デフォルト値はfalseで、バブリングまたはターゲットフェーズでイベントが受取られる。

戻り値 第2引数に受取ったリスナー。
on()メソッド
文法 on(type, listener, scope, once, data, useCapture)
概要 第1引数のイベントに第2引数のリスナーを加える。残りの引数で、さらに細かな定めが加えられる。
引数

type − イベントの名前(種類)を定める文字列。

listener − イベントが起こったときに呼出す関数またはオブジェクト。リスナーとしてオブジェクトを加えると、そのhandleEvent()メソッドが呼出される。

scope − リスナーを実行するスコープのオブジェクト。デフォルトの参照はつぎのとおり。

  • 関数
    イベントを加えたオブジェクトでEvent.currentTargetプロパティの参照。
  • オブジェクト
    handleEvent()メソッドをもつリスナーに定めたオブジェクト。

oncetrueを渡すと、リスナーは1度だけ呼出されたら削除される。デフォルト値はfalse

data − リスナーとして呼出す関数の第2引数に渡す任意の値。

useCapture − バブリングするイベントでtrueを渡すと、リスナーはキャプチャフェーズでイベントを捉える。デフォルト値はfalseで、バブリングまたはターゲットフェーズでイベントが受取られる。

戻り値 リスナーとして呼出される関数。第2引数に受取った関数から、他の引数にもとづいて新たにつくられる。
removeEventListener()メソッド
文法 removeEventListener(type, listener, useCapture)
概要 第1引数のイベントに加えた第2引数のリスナーを除く。
引数

type − イベントの名前(種類)を定める文字列。

listener − イベントのリスナーから除く関数またはオブジェクト。

useCapture − リスナーがイベントのキャプチャフェーズに加えられたかどうかを示すブール(論理)値。デフォルト値はfalseで、バブリングかターゲットフェーズのリスナーを除く。

戻り値 なし。
off()メソッド
文法 off(type, listener, useCapture)
概要 第1引数のイベントに加えた第2引数のリスナーを除く。EventDispatcher.on()メソッドに対応して定められ、内部的にEventDispatcher.removeEventListener()メソッドを参照する。
引数

type − イベントの名前(種類)を定める文字列。

listener − イベントのリスナーから除く関数またはオブジェクト。

useCapture − リスナーがイベントのキャプチャフェーズに加えられたかどうかを示すブール(論理)値。デフォルト値はfalseで、バブリングかターゲットフェーズのリスナーを除く。

戻り値 なし。
removeAllEventListeners()メソッド
文法 removeAllEventListeners(type)
概要 引数のイベントに加えたすべてのリスナーを除く。
引数

type − イベントの名前(種類)を定める文字列。引数を省くと、すべてのイベントのリスナーが除かれる。

戻り値 なし。
hasEventListener()メソッド
文法 hasEventListener(type)
概要 指定したイベントにリスナーが加えられているかどうかをブール(論理)値で返す。バブリングとターゲットおよびキャプチャのフェーズは問わない。
引数

type − イベントの名前(種類)を定める文字列。

戻り値 イベントにリスナーがあればtrue、なければfalseのブール値。
willTrigger()メソッド
文法 willTrigger(type)
概要 指定したイベントリスナーの備わるオブジェクトがイベントフロー上にあるかどうかをブール(論理)値で返す。バブリングするイベントでは、参照したオブジェクトだけでなく、フローの上層のオブジェクトすべてが調べられる。
引数

type − イベントの名前(種類)を定める文字列。

戻り値 イベントにリスナーがあればtrue、なければfalseのブール値。
dispatchEvent()メソッド
文法 dispatchEvent(eventObj, bubbles, cancelable)
概要 引数に定めたイベントをリスナーに配信する。
引数

eventObj − イベントの名前(種類)を定める文字列、またはその文字列をtypeプロパティとしてもつオブジェクト。

引数に渡すオブジェクトは、Eventクラスでつくるのがよい。文字列で渡すと、メソッドの参照するオブジェクトがそのイベントのリスナーをもつか、イベントがバブリングする場合にかぎり、その値をEvent.typeプロパティにもつEventオブジェクトが内部的につくられて送られる。

bubbles − 第1引数のeventObjに文字列が渡された場合に、イベントがバブリングするかどうかを示すブール値。EventオブジェクトのEvent.bubblesプロパティの値に定められる。

cancelable − 第1引数のeventObjに文字列が渡された場合に、イベントがキャンセルできるかどうかを示すブール値。EventオブジェクトのEvent.cancelableプロパティの値に定められる。

戻り値

送るイベントにEvent.preventDefault()メソッドが呼出されていたらfalse、そうでなければtrueを返す[*1]

initialize()メソッド
文法 EventDispatcher.initialize(target)
概要 [静的] EventDispatcherクラスがイベントリスナーを扱うためのメソッド群を、引数のオブジェクトに実装(Mixin)する。
引数

target − EventDispatcherクラスのメソッドを備えさせるクラス(コンストラクタ関数)のプロトタイプオブジェクト(Function.prototypeプロパティ)またはインスタンス。

戻り値 なし。
toString()メソッド
文法 toString()
概要 EventDispatcherインスタンスの文字列表現を返す。
引数 なし。
戻り値

つぎの文字列。

[EventDispatcher]

[*1] EaselJS 0.8.1で、DOM Level 2 Event Modelと互換性をもたせるために変更されました(MDN「EventTarget.dispatchEvent()メソッド」参照)。

EasleJS 0.8.0では、送られるイベントオブジェクトのEvent.defaultPreventedプロパティの値が返されました。0.8.1のEventDispatcher.dispatchEvent()メソッドの実装はつぎのとおりで、戻り値の真偽が逆になるのでご注意ください。

EventDispatcher.prototype.dispatchEvent = function(eventObj, bubbles, cancelable) {
  // ...[中略]...
  return !eventObj.defaultPrevented;
};


説明

EventDispatcherクラスは、イベントごとに加えられたリスナーを取りまとめ、イベントが起こったときそれぞれのリスナーに配信します。DisplayObjectは、EventDispatcherクラスを継承してイベントリスナーを扱います。他方、Tickerクラスは静的メソッドEventDispatcher.initialize()により、EventDispatcherクラスの備えるメソッド群をクラスのプロトタイプオブジェクトに加えています。

EventDispatcher.addEventListener()メソッドでイベントに加えたリスナー関数は、「グローバル・オブジェクト」上で呼出されます。したがって、Webブラウザでは、リスナー関数の中のthis参照はWindowオブジェクトを指します。関数の中のthis参照を変えたいときには、EventDispatcher.addEventListener()メソッドの第2引数に渡すリスナーにFunction.bind()メソッドで参照すべきオブジェクトを定めます(ただし、「EaselJSのイベントリスナーにFunction.bind()メソッドを適用するとリスナー内から削除できない」ことにご注意ください)。

EventDispatcher.on()メソッドは、第2引数の関数から新たなリスナー関数をつくって第1引数のイベントに加えます。呼出されるリスナー関数の中のthis参照は、第3引数で定まります。デフォルトでは、リスナーが関数の場合はメソッドの参照するオブジェクト、リスナーがオブジェクトのときはそのオブジェクトになります。メソッドの戻り値は、新たにつくられたリスナー関数です。イベントリスナーを除くときには、この関数の参照を渡さなければなりません。

リスナーにオブジェクトを加えると、そのオブジェクトに備わるhandleEvent()メソッドが呼出されます。たとえば、Stageクラスには内部的にhandleEvent()メソッドが定められており、Stage.update()メソッドを呼出します(後述「実装」参照)。したがって、Ticker.tickイベントのリスナーにStageオブジェクトを加えれば、Stage.update()メソッドがイベントごとに繰返し呼出せます。

静的メソッドEventDispatcher.initialize()に、クラス(コンストラクタ関数)のプロトタイプオブジェクト(Function.prototypeプロパティ)またはインスタンスを渡して呼出すと、EventDispatcherクラスのつぎの8つのメソッドが備わります(厳密には、後述「実装」のとおり、内部的なメソッドがもうひとつ加えられます)。

  • EventDispatcher.addEventListener()
  • EventDispatcher.on()
  • EventDispatcher.removeEventListener()
  • EventDispatcher.off()
  • EventDispatcher.removeAllEventListeners()
  • EventDispatcher.hasEventListener()
  • EventDispatcher.dispatchEvent()
  • EventDispatcher.willTrigger()

つぎのスクリプトは、インスタンス(instance)のDisplayObject.clickイベント("click")に、EventDispatcher.addEventListener()メソッドでリスナー関数(clickHandler())を加えます。すると、インスタンスをクリックしたとき、イベントリスナーが呼出されます。リスナー関数は、引数に受取ったイベントオブジェクトのEvent.targetプロパティから、クリックしたインスタンスの参照(instance)を得ます。そして、処理を終えた後、EventDispatcher.removeEventListener()メソッドでインスタンスのこのイベントからリスナーを除いています。したがって、この後インスタンスをクリックしても、関数は呼出されません。

instance.addEventListener("click", clickHandler);
function clickHandler(eventObject) {
  var instance = eventObject.target;
  // 処理内容
  instance.removeEventListener("click", clickHandler);
}

つぎのスクリプトは、インスタンス(instance)のDisplayObject.clickイベント("click")に、EventDispatcher.on()メソッドでリスナー関数(clickHandler())を加えます。インスタンスをクリックしたとき、イベントリスナーは第3引数のオブジェクト(data)をthis参照として呼出されます。また、メソッドの第5引数に与えた値(30)は、リスナー関数が第2引数として受取ります。

リスナー関数(clickHandler())は、第1引数に受取ったイベントオブジェクト(eventObject)からクリックしたインスタンスの参照(instance)を得て、その角度を第2引数(angle)の値回します。そして、this参照のカウンタのプロパティ(count)を1加算して、値が1を超えるとEventDispatcher.removeEventListener()メソッドでインスタンスのこのイベントからリスナーを除きます[*2]。したがって、インスタンスが回るのは、クリック2回だけになります。

EventDispatcher.removeEventListener()メソッドの第2引数に渡すリスナー関数が、this参照に定めたオブジェクトのプロパティ(listener)から取出されていることにご注目ください。なお、コードの初めの4行を関数内に書いて、EventDispatcher.on()メソッドの第3引数に引数に渡すオブジェクト(data)をローカル変数に納めたとしても、オブジェクトの参照は保たれます。

var data = {};
var listener = instance.on("click", clickHandler, data, false, 30);
data.listener = listener;
data.count = 0;
function clickHandler(eventObject, angle) {
  var instance = eventObject.target;
  instance.rotation += angle;
  if (++this.count > 1) {
    instance.removeEventListener("click", this.listener);
  }
}

EventDispatcher.dispatchEvent()メソッドを用いたサンプルは、「EaselJS NEXT: Event.set()メソッドでオブジェクトにプロパティをまとめて加える」の「」をご参照ください。

[*2] 呼出されたリスナー関数自身をイベントから除くには、Event.remove()メソッドを用いることもできます(後述「実装」のEventDispatcher.on()メソッド参照)。

ただし、Event.remove()メソッドは直ちにイベントリスナーを消さないことにご注意ください。そのイベントリスナーをもう呼出さないというフラグが与えられるだけです。内部的には、つぎにイベントが配信されるとき、フラグのついたリスナーが除かれます。そのため、Event.remove()メソッドを呼出したすぐ後にEventDispatcher.hasEventListener()メソッドでリスナーがあるか調べると、trueが返されてリスナーは残っています。


実装

EventDispatcherクラスの以下の3つのメソッドについて、バージョン0.7.0にもとづき、その実装をかいつまんでご説明します。

EventDispatcher.addEventListener()メソッド
イベントリスナーは、内部的なプロパティでキャプチャフェーズ(_captureListeners)とそれ以外(_listeners)に分けてオブジェクトとして納められています。それらのオブジェクトには、イベント名のプロパティに配列が与えられ、リスナーはその配列エレメントに加えられます。

そこで、EventDispatcher.addEventListener()メソッドは、第3引数(useCapture)に応じてリスナーが納められたオブジェクトの参照を変数(listeners)に得て、指定のイベント(type)のプロパティで定められた配列(arr)を取出します。そして、メソッドが第2引数に受取ったリスナー(listener)を、その配列に納めています。EventDispatcher.addEventListener()メソッドは、加えたリスナーを返します。

EventDispatcher.prototype.addEventListener = function(type, listener, useCapture) {
  var listeners;
  if (useCapture) {
    listeners = this._captureListeners = this._captureListeners || {};
  } else {
    listeners = this._listeners = this._listeners || {};
  }
  var arr = listeners[type];
  // ...[中略]...
  if (!arr) { listeners[type] = [listener]; }
  else { arr.push(listener); }
  return listener;
};

なお、リスナーを納めるオブジェクトがまだないときは、新たなオブジェクトがつくられてプロパティ(_captureListenersまたは_listeners)に与えられます。論理和演算子||を用いた初期値の代入については、「論理演算子を使った条件判定」02「初期化されていない変数に値を与える」をご参照ください。

EventDispatcher.on()メソッド

EventDispatcher.on()メソッドは、第2引数のリスナー(listener)から、他の引数にもとづく新たな関数をつくり、第1引数(type)のイベントのリスナーとして加えます。まず、第2引数のリスナーがhandleEvent()メソッドをもつオブジェクトのときは、そのメソッドをリスナー関数とします。スコープ(scope)のデフォルト値は、リスナーオブジェクトです。つぎに、第2引数が関数の場合には、スコープのデフォルト値をthis参照とします。

そして、改めて内部的にEventDispatcher.addEventListener()メソッドを呼出します。第1引数に受取ったイベントはそのまま渡し、リスナーを名前のない関数で定めています。その中でリスナー(listener)はFunction.call()メソッドで、スコープ(scope)をthis参照として呼出します(「SoundJSで関数にスコープを定める ー proxy()メソッド」の「実装」参照)。また、受取った第4引数(once)がtrueのときは、Event.remove()メソッドでイベントリスナーから除きます。

EventDispatcher.addEventListener()メソッドは、イベントに加えたリスナーを返すので、その関数をEventDispatcher.on()メソッドの戻り値とします。

EventDispatcher.prototype.on = function(type, listener, scope, once, data, useCapture) {
  if (listener.handleEvent) {
    scope = scope || listener;
    listener = listener.handleEvent;
  }
  scope = scope || this;
  return this.addEventListener(type, function(evt) {
      listener.call(scope, evt, data);
      once && evt.remove();
    }, useCapture);
};

なお、論理積演算子&&によるメソッドの呼び出しについては、「論理演算子を使った条件判定」01「関数があったら呼出す」をご参照ください。

EventDispatcher.initialize()メソッド

EventDispatcher.initialize()メソッドは、引数に渡されたオブジェクトに、EventDispatcherクラスのイベントリスナーを扱うメソッドを加えます。EventDispatcherクラスのプロトタイプオブジェクト(Function.prototypeプロパティ)から9つのメソッドの参照を、同じ名前で引数のオブジェクトに与えます[*3]。なお、_dispatchEvent()メソッドは、EventDispatcher.dispatchEvent()メソッドから内部的に呼出されます。

EventDispatcher.initialize = function(target) {
  target.addEventListener = EventDispatcher.prototype.addEventListener;
  target.on = EventDispatcher.prototype.on;
  target.removeEventListener = target.off = EventDispatcher.prototype.removeEventListener;
  target.removeAllEventListeners = EventDispatcher.prototype.removeAllEventListeners;
  target.hasEventListener = EventDispatcher.prototype.hasEventListener;
  target.dispatchEvent = EventDispatcher.prototype.dispatchEvent;
  target._dispatchEvent = EventDispatcher.prototype._dispatchEvent;
  target.willTrigger = EventDispatcher.prototype.willTrigger;
};

Tickerクラスは初期化されるときに、クラス(コンストラクタ)をEventDispatcher.initialize()メソッドに渡して、EventDispatcherクラスのメソッドを実装しています

createjs.EventDispatcher.initialize(Ticker);

Stage.handleEvent()メソッド(内部実装)

ご参考までに、ドキュメントに掲載されず、内部的に定められたStageクラスのメソッドhandleEvent()の実装をご紹介します。Ticker.tickイベントにリスナーとして加えられ、引数に受取ったイベントオブジェクトのtypeプロパティ値が"tick"であることを確かめると、Stage.update()メソッドが呼出されます。

Stage.prototype.handleEvent = function(evt) {
  if (evt.type == "tick") { this.update(evt); }
}

[*3] このようにしてクラスのメソッドを他のクラスに加える手法は「Mixin」と呼ばれます。すでに他のクラスを継承しているとき、さらに別のクラスのプロパティやメソッドが使いたい場合に便利です。



作成者: 野中文雄
更新日: 2015年6月2日 EaselJS 0.8.1にもとづいて改訂。タイトルのバージョンも改めた。
更新日: 2013年12月22日 タイトルを「EaselJSでイベントリスナーを扱うEventDispatcherクラス」から改題。EventDispatcher.willTrigger()メソッドを追加
更新日: 2013年10月12日 EaselJS 0.7.0にもとづく改訂。
更新日: 2013年8月29日 EaselJS次期バージョン候補(NEXT)にもとづいて、大幅に改定。「実装」のEventDispatcher.dispatchEvent()メソッドは除き、EventDispatcher.on()メソッドに替えた。
更新日: 2012年2月25日 CreateJSの新バージョンのリリースにともなう記述の変更およびリンク追加。また、Stage.handleEvent()メソッドの説明と実装を加えた。
更新日: 2013年2月7日「EaselJSのイベントリスナーにFunction.bind()メソッドを適用するとリスナー内から削除できない」へのリンク追加とそれにともなう若干の修正。
作成日: 2013年2月2日


Copyright © 2001-2015 Fumio Nonaka.  All rights reserved.