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

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

- `event.stopPropagation()`
- `event.preventDefault()`

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

1. Capturing Phase
1. Target Phase 
1. Bubbling Phase

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/e9caead3-41dc-3f03-10ee-29671f551445.png)
[引用元：w3.org UI Events](https://www.w3.org/TR/uievents/#event-flow)

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

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

## JavaScriptのイベントはいつ発火するのか？
[上記](#javascriptのevent-phases)でみたように、イベントには3つのフェーズがあるが、実際にイベントが発火するのは、基本的に**Bubbling Phase[※1](#1)**。

```js: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()`](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)の引数`useCapture`が`false`である場合、Bubbling Phaseでイベントが発火する。
`addEventListener()`の引数`useCapture`が`true`である場合、Capturing Phaseでイベントが発火する。
引数`useCapture`は省略可能であり、デフォルトでは`false`が設定されているものとして動く。
※モダンなJavaScriptフレームワーク（React、Vue、Angularなど）では`addEventListener`でイベント定義をする事はなく、falseの状態でイベント定義する事になるので、Bubbling Phaseでイベントが発火すると考えても問題ない（と思っている）。

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

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

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

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

```js: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`の要素：クリックするとチェックが入る

```html:event-prevent.html
<html>
  <body>
    <a id="link" href="https://google.com">Google</a>
  </body>
</html>
```
```js:event-prevent.js
document.querySelector('link').addEventListener('click', function (event) { 
  event.preventDefault();
});
```

## 参考文献
- https://www.w3.org/TR/DOM-Level-3-Events/