Closed8

イベント伝播

HTMLGOHTMLGO

こういうやつ

    <div class="e1">
        e1
        <div class="e2">
          e2
          <div class="e3">e3</div>
        </div>
      </div>
HTMLGOHTMLGO

デフォルトでは、子から親にバブリングフェーズでイベントが伝わっていく

e1クリック → e1
e2クリック → e2 → e1
e3クリック → e3 → e2→ e1


const e1 = document.querySelector(".e1");
const e2 = document.querySelector(".e2");
const e3 = document.querySelector(".e3");

const options = {
  e1: {
    passive: false,
    capture: false,
  },
  e2: {
    passive: false,
    capture: false,
  },
  e3: {
    passive: false,
    capture: false,
  },
};

const call = (e) => {
  //   e.stopPropagation();
  alert(e.currentTarget.className);
};

e1.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e1 }
);
e2.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e2 }
);
e3.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e3 }
);
HTMLGOHTMLGO

stopPropagation で伝播が止まる。
バブリングされないようになる。

  e.stopPropagation();
  alert(e.currentTarget.className);
};

e1クリック → e1
e2クリック → e2
e3クリック → e3

HTMLGOHTMLGO
const options = {
  e1: {
    passive: false,
    capture: true,
  },
  e2: {
    passive: false,
    capture: true,
  },
  e3: {
    passive: false,
    capture: true,
  },
};

const call = (e) => {
  //   e.stopPropagation();
  alert(e.currentTarget.className);
};

e1.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e1 }
);
e2.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e2 }
);
e3.addEventListener(
  "click",
  (e) => {
    call(e);
  },
  { ...options.e3 }
);

capture: true
にしたらキャプチャリングフェーズでイベントが実行される

e1クリック → e1
e2クリック → e1 → e2
e3クリック → e1 → e2 → e3

で、さらにstopPropagationを適用

const call = (e) => {
  e.stopPropagation();
  alert(e.currentTarget.className);
};

伝播が止まる
e1クリック → e1
e2クリック → e1
e3クリック → e1

HTMLGOHTMLGO

preventDefaultはイベントに備わった動作をキャンセルするもの。
イベントの伝播は止まらない

const call = (e) => {
  e.preventDefault(); // 伝播は止まらない!
  alert(e.currentTarget.className);
};
HTMLGOHTMLGO

こういうことをすると、以下のエラーが出る。

Unable to preventDefault inside passive event listener invocation.

{passive:true} の状態ではイベントリスナーの中で
「preventDefaultは使ってません!」という状態なので、矛盾が生じる。
上記によって、パフォーマンスをあげているわけなので。

const options = {
  e1: {
    passive: true,
    capture: false,
  },
  e2: {
    passive: true,
    capture: false,
  },
  e3: {
    passive: true,
    capture: false,
  },
};

const call = (e) => {
  //   e.stopPropagation();
  e.preventDefault();
  alert(e.currentTarget.className);
};
HTMLGOHTMLGO

https://zenn.dev/maronn/articles/js-event-handler#react-で-prevnetdefault-が上手く効かない場合について

そのため、React では wheel イベントや touch イベントなど即時反映が求められるイベントに対して、Passive モードを有効にしています。

Reactでは、デフォで、passive:trueになっているイベントがあるので、
preventDefaultでエラーが出る場合があるので。注意が必要。

このスクラップは2025/01/01にクローズされました