SYNCERのロゴ
アイキャッチ画像

EventTarget - イベントの対象

EventTargetは、イベントを設定するための機能を備えたインターフェイスです。NodeやElement、DocumentWindowなど、イベントを設定できるインターフェイスがこれを継承しています。

概要

名前
EventTarget
IDL
[Constructor,
 Exposed=(Window,Worker,AudioWorklet)]
interface EventTarget {
  void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);
  void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
  boolean dispatchEvent(Event event);
};

dictionary AddEventListenerOptions : EventListenerOptions {
  boolean passive = false;
  boolean once = false;
};

callback interface EventListener {
  void handleEvent(Event event);
};

dictionary EventListenerOptions {
  boolean capture = false;
};
仕様書
https://dom.spec.whatwg.org/#eventtarget

チュートリアル

イベントの設定

ある要素をクリックした時のイベントを設定する例です。

<button id="hoge">SYNCER</button>
// 要素の取得
var element = document.getElementById( "hoge" ) ;

// コールバック関数
var listener = function ( event ) {
	console.log( "クリックしました!!" ) ;
}

// 要素にイベントを追加
element.addEventListener( "click", listener ) ;

// 要素のイベントを削除
element.removeEventListener( "click", listener ) ;

イベント発生の仕組み

イベントが発生すると、子孫から先祖の順番にコールバック関数が呼び出されます。例えば、下記は、それぞれの要素にクリックイベントが設定されている状況で、div#event4をクリックした時の例です。すると、div#event4、div#event3、div#event2、div#event1の順番にコールバック関数が呼び出されます。

<!-- 子孫から先祖(event4 → event3 → event2 → event1)の順番にコールバック関数が呼び出される -->
<div id="event1">
	<div id="event2">
		<div id="event3">
			<!-- ここがイベントの発生元 -->
			<div id="event4">
			</div>
		</div>
	</div>
</div>

もちろん、これは基本であって、イベントの設定によってはこの順番が変わることもあります。その仕組みを後述します。

立場

さて、疑問に思わなければいけないのは「なんでクリックしてないdiv#event1、div#event2、div#event3のコールバック関数が呼び出されてしまうのか?」です。これは、それらがdiv#event4の先祖だからです。レイアウトで重なっていたか、重なっていないかは一切関係ありません。レイアウトは捨てて下さい。ツリー構造上の先祖と子孫の関係だけを考えて下さい。

要素立場説明
div#event1先祖ついでに、コールバック関数が呼び出される。
div#event2先祖ついでに、コールバック関数が呼び出される。
div#event3先祖ついでに、コールバック関数が呼び出される。
div#event4発生元イベントの発生元だから、コールバック関数が呼び出される。

何故、イベントが発生した時、発生元の先祖に設定してあるコールバック関数が呼び出されてしまうのか。これは、イベントフェイズという概念を知れば理解できます。

イベントフェイズと伝播

イベントが発生すると「伝播」が起こります。伝播とは「伝わる」という意味です。リンゴでもなんでもいいのですが、たった1つの「何か」を次から次へ渡していく、という画をイメージすると分かりやすいと思います。伝播を受け取ると、設定されているコールバック関数が呼び出されます。伝播の現在位置によって、3つの段階(フェイズ)に分けることができます。これらをまとめて、イベントフェイズといいます。

順番名前説明伝播の順番
1キャプチャリングフェイズ伝播がイベントの発生元に辿り着く前の段階。先祖から子孫の方向に伝播する。div#event1 → div#event2 → div#event3
2ターゲットフェイズ伝播がイベントの発生元に辿り着いた段階。div#event4
3バブリングフェイズ伝播がイベントの発生元に辿り着いた後の段階。子孫から先祖の方向に伝播する。div#event3 → div#event2 → div#event1

先にまとめてしまうと、立場が「先祖」の場合、キャプチャリングフェイズ、または、バブリングフェイズでコールバック関数が実行されます。立場が「発生元」の場合、ターゲットフェイズでコールバック関数が実行されます。

キャプチャリングフェイズ

イベントが発生すると、一番最初に、先祖から子孫の方向に伝播します。伝播がイベントの発生元に辿り着く前までの段階を、キャプチャリングフェイズといいます。例では、一番の先祖であるWindowから始まり、Documentを経て、、

// div#event4に辿り着く前までがキャプチャリングフェイズ
(省略) → div#event1 → div#event2 → div#event3 → div#event4(イベントの発生元)

キャプチャリングフェイズでは、伝播を受けてもコールバック関数は呼び出されません。つまり、この段階ではdiv#event1、div#event2、div#event3のコールバック関数が呼び出されません。

これはデフォルトの話です。「キャプチャリングフェイズの段階でコールバック関数を実行する」と、イベントの登録時に設定することができます。

ターゲットフェイズ

キャプチャリングフェイズを経て、伝播がイベントの発生元に辿り着いた段階を、ターゲットフェイズといいます。ターゲットフェイズでは、イベントの発生元のコールバック関数が呼び出されます。例では、div#event4のコールバック関数が呼び出されます。イベントの発生元は、伝播の行き止まりです。つまり、これ以上、子孫の方向には伝播しません。

// div#event4に辿り着いた時点がターゲットフェイズ
(省略) ← div#event1 ← div#event2 ← div#event3 ← div#event4(イベントの発生元)

バブリングフェイズ

行き止まり(イベントの発生元)まで辿り着いた伝播は、今度は逆方向、すなわち、子孫から先祖の方向に進みます。この段階を、バブリングフェイズと言います。バブリングフェイズは、イベントの発生元(div#event4)を含みません。

// div#event4に辿り着いた時点がターゲットフェイズ
(省略) ← div#event1 ← div#event2 ← div#event3 ← div#event4(イベントの発生元)

バブリングフェイズでは、伝播を受けてコールバック関数が呼び出されます。つまり、例では、div#event3、div#event2、div#event1と、伝播を受けた順番に、それぞれに設定してあるコールバック関数が呼び出されるというわけです。

これはデフォルトの話です。「バブリングフェイズの段階ではコールバック関数を実行しない」と、イベントの登録時に設定することができます。

プロパティ

固有のプロパティはありません。

メソッド

addEventListener()

1つのターゲット(要素など)に対して、1つのリスナー(コールバック関数)を追加登録します。

dispatchEvent()

ターゲットに対して、任意のイベントを発生させます。

removeEventListener()

ターゲットに設定してあるイベントを削除します。

定数

固有の定数はありません。

イベント

固有のイベントハンドラはありません。

サポート状況

クリックすると、バージョンごとの対応状況を確認できます。

FeaturesChromeFirefoxSafariEdgeIEOperaiOS SafariAndroid
EventTarget 9+
addEventListener() 9+
dispatchEvent() 9+
removeEventListener() 9+
  • Twitterでシェア
  • Facebookでシェア
  • Google+でシェア
  • はてなブックマークでシェア
  • pocketに保存
  • LINEでシェア
更新履歴
2017年10月3日 (火)
コンテンツを公開しました。