Event.preventDefaultとEvent.stopPropagationの違いを調べた

テックブログ初めての投稿になります! フロントエンドエンジニアの君田(きみた)です。

タイトル通り、Event.preventDefaultとEvent.stopPropagationの違いを調べたので記事にしようと思います。

調べようと思ったきっかけ

自分自身、preventDefaultstopPropagationの使いどころと、違いなどが曖昧だったためです。 ふるさとチョイス内ではaddEventListenerを使用している箇所が多く、 preventDefaultstopPropagationを使用している箇所とそうでないところがあったため疑問に思ったためです。

preventDefaultについて

preventDefaultを実行した場合、任意のイベントに対して、ブラウザで既定されている動作をキャンセルさせることができます。

まずはpreventDefaultを使用しない場合の例を記載します。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <label for="id-checkbox">チェックボックス:</label>
    <input type="checkbox" id="id-checkbox" />
  </body>
  <script>
    const target = document.querySelector("#id-checkbox");
    target.addEventListener("click", (event) => {
      console.log(document.querySelector("#id-checkbox").checked);
    });
  </script>
</html>

チェックボックスをクリックしたときに、チェックボックスのchecked属性の値をコンソールに表示させるサンプルです。 期待通りクリックするとチェックボックスの表示が切り替わり、checked属性の値も切り替わっています。

preventDefaultを使用した場合はどうでしょうか?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <label for="id-checkbox">チェックボックス:</label>
    <input type="checkbox" id="id-checkbox" />
  </body>
  <script>
    const target = document.querySelector("#id-checkbox");
    target.addEventListener("click", (event) => {
      event.preventDefault();
      console.log(document.querySelector("#id-checkbox").checked);
    });
  </script>
</html>

チェックボックスをクリックしても表示が切り替わりません。 また、checked属性の値は最初のクリックでは切り替わりますが、それ以降のクリックでは値は変わりません。

このように意図的にブラウザで既定されている動作をキャンセルさせることができます。 チェックボックスだけでなく、aタグによる画面遷移などもキャンセルできます。

stopPropagationについて

要素のクリックなどで発生したイベントの伝播を阻止します。 こちらも実際の例で見てみましょう。

まずはstopPropagationを使用しない場合の例を記載します。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <div id="parent">
      <button id="child-1">child-1</button>
      <button id="child-2">child-2</button>
    </div>
    <script>
      const parent = document.getElementById('parent');
      const child1 = document.getElementById('child-1');
      const child2 = document.getElementById('child-2');
      parent.addEventListener('click', (event) => {
        alert('parent')
      })
      child1.addEventListener('click', (event) => {
        alert('child1')
      })
      child2.addEventListener('click', (event) => {
        alert('child2')
      })
    </script>
  </body>
</html>

child1, child2のボタンをクリックした場合、そのボタン自体に登録されているイベントリスナーが反応した後に、 parentに登録されているイベントリスナーが反応しています。 これは、クリックイベントが伝播しているためです。 child1, child2で発生したクリックイベントがparentにまで伝わり、どちらのイベントリスナーも反応しています。 このイベントの伝播をキャンセルすることができるのが、stopPropagationです。

こちらも実際の例で見てみましょう。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <div id="parent">
      <button id="child-1">child-1</button>
      <button id="child-2">child-2</button>
    </div>
    <script>
      const parent = document.getElementById("parent");
      const child1 = document.getElementById("child-1");
      const child2 = document.getElementById("child-2");
      parent.addEventListener("click", (event) => {
        alert("parent");
      });
      child1.addEventListener("click", (event) => {
        event.stopPropagation();
        alert("child1");
      });
      child2.addEventListener("click", (event) => {
        alert("child2");
      });
    </script>
  </body>
</html>

child1のイベントリスナー内にevent.stopPropagation();の記述を追加しました。 実装の動作で見てもわかるとおり、child1のクリックイベントはparentに伝播せず、 parentのイベントリスナーは反応していません。 child2の方は最初の例と同じく、どちらのイベントリスナーも反応しています。

最後に

preventDefaultとstopPropagationの違いを説明してきました。 自分自身理解が甘いところがあったので、今後も気づきなどあればテックブログにガンガン投稿していこうと思います!

弊社トラストバンクでは様々な職種で絶賛採用中です! 気になった方、是非お気軽にご連絡ください!

www.wantedly.com