【図解】イベントを止めるメソッド preventDefault / stopPropagation

Javascriptにはある動作をキャンセルする『preventDefault』『stopPropagation』メソッドが存在します。今回はこのメソッドを使う理由をコードを交えて解説していきます。

preventDefaultとstopPropagationとは?

両者ともイベントの処理を止めるという意味では同じですが、役割が異なるので整理しておきましょう。

  • preventDefault:イベント本来の挙動をキャンセルする
  • stopPropagation:イベントの伝播をキャンセルする

これら2つのメソッドを理解するためには、まずJavascriptのイベント発生で何が起きているのか知る必要があります。

イベントが伝わる様子を確認してみよう

まずはイベントが伝わる様子を確認してみましょう。
VSCodeなどのテキストエディタで下記のコードを貼り付けてください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>JSPractice</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <div id="grand-parent" style="background-color: grey; padding: 20px">
    <div id="parent" style="background-color: lightgrey; padding: 10px">
       <button>
          <a href="https://spesperficio.com/" id="child">子要素だよ</a>
      </button>
    </div>
  </div>
  <script src="script.js"></script>
</body>
document.addEventListener("DOMContentLoaded", function () {
  document.getElementById("child").addEventListener(
    "click",
    function (e) {
      window.alert("子のリスナーだよ!!");
    },
    false
  );
  document.getElementById("parent").addEventListener(
    "click",
    function (e) {
      window.alert("親のリスナーだよ!!");
    },
    false
  );
  document.getElementById("grand-parent").addEventListener(
    "click",
    function (e) {
      window.alert("祖先のリスナーだよ!!");
    },
    false
  );
});

余談ですが、イベントリスナー内の関数の引数として、eという『イベントオブジェクト』を受け取っています。

イベントオブジェクトには、イベント発生時の情報が格納されています。

さてここまでで下画像のようになっているかと思います。濃いグレーが祖先要素、薄いグレーが親要素、ボタンの部分が子要素です。

Javascriptでは子要素をクリックすると、「このリスナーだよ!!」とアラートが出るような設定がされています。

試しに子要素をクリックして挙動を確認してみましょう。

ふふふ….

子の要素のアラート
親の要素のアラート
祖先の要素のアラート

子の要素をクリックしたのに、関係の無い親・祖先要素のアラートまで出てしまいました。なぜでしょう??

イベントが発生する流れを理解しよう

先程の現象を『バブリング』と呼んでいます。イベントがイベント発生もとの要素から親要素まで伝わる様子が、水の中の泡の動きに似ていることからこのような名称となりました。

そもそも親要素とか子要素ってなんなん?って思うかもしれませんので説明しておきます。DOMの最上位には『window』というオブジェクトがいます。

ある子要素でイベントが発生したとします。今回はaタグがイベントの発生元としておきましょう。

イベント発生の流れ① キャプチャフレーズ

イベントが発生すると最上位のwindowはイベント発生元をツリーをたどって、探しにいきます。このようにツリーを辿る段階を『キャプチャフレーズ』と呼んでいます。

イベント発生の流れ② ターゲットフレーズ

windowはツリーをたどり、イベントの発生元を特定します。この段階を『ターゲットフレーズ』と呼びます。

イベント発生の流れ③ バブリングフレーズ

子要素でイベントが発生することにより、イベントを持っている(例:クリックするとアラートが出る)親要素のイベントも発生します。

この段階を『バブリングフレーズ』と呼びます。

このようにイベントが発生した場合、下記3ステップを踏んでいるのです。

  1. windowがツリーをたどり、
  2. イベントの発生元を特定し、
  3. 他のイベントを持つ親要素のイベントも発火する

バブリングの挙動を確かめよう

では、冒頭で書いたコードに戻って、再度バブリングの挙動を確認しましょう。

まず子要素(=白)をクリックした場合、親(=薄いグレー)と祖先(=濃いグレー)の要素をクリックしていないにも関わらずイベントが発生します。

では今度は親要素をクリックしてみましょう。そうすると子の要素のイベントは発生せず、親と祖先のみイベントが発火します。

最後に祖先要素をクリックすると、親と子要素のイベントは発火せず、祖先要素のイベントのみ発火します。

バブリングをキャンセルするstopPropagationメソッド

ここまでイベントが伝わる(伝播する)際に予期せぬバブリングが起きてしまうケースを見てきました。

バブリングを発生させないようにするメソッドがJavascriptには用意されています。それが『stopPropagation』メソッドです。

冒頭で紹介したコードを交えて、挙動を確認してみましょう。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>JSPractice</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>
  <div id="grand-parent" style="background-color: grey; padding: 20px">
    <div id="parent" style="background-color: lightgrey; padding: 10px">
       <button>
          <a href="https://spesperficio.com/" id="child">子要素だよ</a>
      </button>
    </div>
  </div>
  <script src="script.js"></script>
</body>
document.addEventListener("DOMContentLoaded", function () {
  document.getElementById("child").addEventListener(
    "click",
    function (e) {
      window.alert("子のリスナーだよ!!");
      //↓メソッドを追加!!
      e.stopPropagation();
    },
    false
  );
  document.getElementById("parent").addEventListener(
    "click",
    function (e) {
      window.alert("親のリスナーだよ!!");
    },
    false
  );
  document.getElementById("grand-parent").addEventListener(
    "click",
    function (e) {
      window.alert("祖先のリスナーだよ!!");
    },
    false
  );
});

子要素のリンクをクリックすると、「子のリスナーだよ!!」のアラートが出て、ページ遷移します。

子のイベントのみ発生

本来であればバブリングが起きて、親と祖先のイベントが発生しますが、stopPropagationメソッドによりキャンセルされているわけです。

既定の動作をキャンセルするpreventDefaultメソッド

子要素をクリックすると、指定したリンクへページ遷移しますよね?

場合によっては、クリックしてもページ遷移して欲しくない場合もあります。そのような時に使うのが、『preventDefault』メソッドです。

先程追加したe.stopPropagation()を、e.preventDefault()に変更して挙動を確認してみましょう。

document.addEventListener("DOMContentLoaded", function () {
  document.getElementById("child").addEventListener(
    "click",
    function (e) {
      window.alert("子のリスナーだよ!!");
      //↓メソッドを追加!!
      e.preventDefault();
    },
    false
  );
  document.getElementById("parent").addEventListener(
    "click",
    function (e) {
      window.alert("親のリスナーだよ!!");
    },
    false
  );
  document.getElementById("grand-parent").addEventListener(
    "click",
    function (e) {
      window.alert("祖先のリスナーだよ!!");
    },
    false
  );
});

子要素をクリックすると、子、親、祖先のイベントが発生しますが、ページ遷移は実行されません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です