Event Phases(イベントフェーズ)について Eventの発火順番とEventメソッド


はじめに

Webの話では必須なEventに関する重要な概念Event Phasesについてまとめる。
その上でよく使われる以下について、それが何をするものなのかをまとめる。

  • event.stopPropagation()
  • event.preventDefault()

Event Phases

イベントにはライフサイクルという概念があり3つに区分される。

  1. Capturing Phase
  2. Target Phase
  3. Bubbling Phase


引用元:w3.org UI Events

上記の図と合わせてライフサイクルにおける各フェーズの意味については以下。

  1. Capturing Phase:イベントが発生した場合にwindowからイベントターゲットまで下に向かって進んでいくフェーズ
  2. Target Phase:イベントターゲットに到達したフェーズ(状態)
  3. Bubbling Phase:その後、元進んできた所を順に戻っていくフェーズ

JavaScriptのイベントはいつ発火するのか?

上記でみたように、イベントには3つのフェーズがあるが、実際にイベントが発火するのは、基本的にBubbling Phase※1

event-phase.js
document.addEventListener('click', function () {
  console.log('The document was clicked');
});

document.body.addEventListener('click', function () {
  console.log('The body element was clicked');
});

そのため上記のようなevent listenerを定義すると、console上には以下のように出力される。
documentとはhtml全体でありhtml > bodyの包含関係である。

The body element was clicked
The document was clicked

※1

正確にはaddEventListener()の引数useCapturefalseである場合、Bubbling Phaseでイベントが発火する。
addEventListener()の引数useCapturetrueである場合、Capturing Phaseでイベントが発火する。
引数useCaptureは省略可能であり、デフォルトではfalseが設定されているものとして動く。
※モダンなJavaScriptフレームワーク(React、Vue、Angularなど)ではaddEventListenerでイベント定義をする事はなく、falseの状態でイベント定義する事になるので、Bubbling Phaseでイベントが発火すると考えても問題ない(と思っている)。

Eventのバブリング(伝播)

以下のHTMLがある時、spanタグの文字列「反応しないでください」の上でマウスを動かしてイベントが発生する場面を考える。
この時、以下のように複数の要素に対して同じevent listenerを設定している場合、Bubbling Phaseで順番に戻る際にそれぞれのイベントが発火する。
つまり、親(div要素)・子(span要素)という関係で見ていくと、子がイベントターゲットなのでその子のイベントがまず発火する。
その後Bubbling Phaseの動きで親要素に移りそこでも同じイベントが定義されているのでそのイベントが発火する、という事が起きる。
※これを「イベントバブリング」と呼ぶ(日本語では「伝播」と言ったりする)。

event-bubbling.html
<html>
  <body>
    <div id="parent">マウスをのせて下さい
      <span id="child">反応しないでください</span>
    </div>
  </body>
</html>
event-bubbling.js
document.querySelector('parent').addEventListener('mousemove', function (event) {
  console.log('マウスをのせて下さい');
});

document.querySelector('child').addEventListener('mousemove', function (event) {
  console.log('反応しないでください');
});

event.stopPropagation() とは

イベントバブリング(伝播)を止める事ができる構文。
具体的には、上記のようなイベントバブリングが起きるケースで、id="parent"のイベントを発火させたくない場合には、event.stopPropagation()を用いる事でそれが実現できる。
※stopPropagationは直訳すると「伝播を停止する」であり、つまりイベントバブリング(伝播)を止めるという事。

event-bubbling.js
document.querySelector('parent').addEventListener('mousemove', function (event) { 
  console.log('マウスをのせて下さい');
});

document.querySelector('child').addEventListener('mousemove', function (event) {
  console.log('反応しないでください');
  event.stopPropagation();  
});

その他のEventメソッド

event.preventDefault() とは

HTMLの各要素の既定の動作を止める事ができる構文。
具体的には、<a>要素をクリックした際にリンクへ飛ぶのを止めるという事がevent.preventDefault()を用いる事で実現できる。
※preventDefaultは直訳すると「デフォルトを妨げる」であり、つまり既定(デフォルト)の動作を止めるという事。

※既定の動作の例

  • aタグの要素:クリックするとhrefで指定したリンク先へ飛ぶ
  • inputタグでtype=checkboxの要素:クリックするとチェックが入る
event-prevent.html
<html>
  <body>
    <a id="link" href="https://google.com">Google</a>
  </body>
</html>
event-prevent.js
document.querySelector('link').addEventListener('click', function (event) { 
  event.preventDefault();
});

参考文献