Javascriptのイベントフェーズについて

今回はJavascriptイベントフェーズについて説明していきたいと思います。

そもそもイベントにはどのようなものがあるのか?

イベントの種類

onClick・・・ボタンやリンクをクリックした時

onSubmit・・・フォームの送信ボタンをクリックした時

onKeyDown・・・キーを押した時

onWheel・・・要素のコンテンツをスクロールした時

他にも色々とあります。 気になる方は調べてみてください。

イベントフェーズ

次にイベントが始まって終わるまでのフェーズは全部で3段階あります。 順を追ってみてみましょう。

  1. キャプチャーフェーズ
  2. ターゲットフェーズ
  3. バブルフェーズ

1. キャプチャーフェーズ

ドキュメントルートツリーからイベントの発生する要素へ伝播するフェーズ

2. ターゲットフェーズ

イベントが発生した要素を特定するフェーズ

3. バブルフェーズ

2で発生したイベントがルートまで遡っていくフェーズ

このようにちゃんと順序があるんですね。

javascriptにおいてイベントの発火を検知するのに addEventListenerを使うことが多いと思います。 addEventListenerはさまざまなイベント処理をハンドリングできるメソッドです。

ターゲットとなる要素.addEventListener(type, listener, useCapture);

type:イベントの種類

listener:主に関数

useCapture:親要素などが同じイベントを持っている時に、キャプチャーフェーズでイベントを発生させたい時は、trueを設定します。そうでなければバブルフェーズで親のイベントが実行されます。デフォルトではfalseとなっています。

実際によくあるケースを例にだしてみます。

モーダルの例

お問い合わせのモーダルはあまり見ないですが、このようにモーダルの中にフォームなど記入するコンテンツがある場合です。

f:id:top_men:20180912191008p:plain

よくある仕様としては閉じるボタンを押すか、背景を押した時にモーダルが閉じるというものです。

実際にコードを書いてみます。※cssは省略します

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>イベントフェーズについて</title>
  <link rel="stylesheet" href="main.css">
</head>
<body>
<div class="modal" id="js-modal">
  <div class="modal_content">
    <form class="form" id="js-form">
      <h2 class="form_title">お問い合わせフォーム</h2>
      <div class="form_content">
        <input type="text" name="name" placeholder="名前を記入してください">
      </div>
      <div class="form_content">
        <input type="email" name="" placeholder="emailを入力してください">
      </div>
      <div class="button"><button>送信</button></div>
    </form>
  </div>
</div>
<script src="main.js"></script>
</body>
</html>
const modal = document.getElementById("js-modal");

modal.addEventListener('click', () => {
  modal.classList.add("is-inactive")
};

f:id:top_men:20180912201421g:plain

背景をクリックしてみてください。 モーダルが消えますよね?

次にフォームのコンテンツ部分をクリックしてください。

これもまたモーダルが消えます。

なぜこうなるかは先ほど説明したバブルフェーズが関わってきます。 モーダルのコンテンツの部分のクリックイベントがモーダルの背景の部分まで伝播してしまっているんですよね。

そのイベントの伝播を防いであげればいいですね。

コードの方をちょっと修正します。

const $modal = document.getElementById("js-modal");
const $modalContents = document.getElementById("js-form");

$modal.addEventListener('click', () => {
  $modal.classList.add("is-inactive");
});

$modalContents.addEventListener('click', (event) => {
  event.stopPropagation();
});

event.stopPropagation();で親へのイベントの伝播を防いでいます。

実際にブラウザで検証した結果です。

f:id:top_men:20180912200209g:plain

モーダルのコンテンツの部分をクリックしてもモーダルが消えなくなりました。

まとめ

イベントの伝播の流れを知っておかないと、上のモーダルの件のように原因が突き止めずらくなると思います。イベントはどの場面でも必ず出てくるのでこの機会にちゃんと頭の中に叩き込んでおきます。