JavaScript 逆引き集 | イベントリスナー削除

JavaScript JavaScript
スポンサーリンク

イベントリスナー削除(DOM) — el.removeEventListener('click', fn)

イベントを「もう受けたくない」瞬間は必ず来ます。removeEventListener は、追加済みのイベントリスナーを安全に外すための基本メソッド。正しく外すには「同じイベント名・同じ関数参照・同じオプション」で照合される点が重要です。


基本の使い方

<button id="buy">購入する</button>
<script>
  const btn = document.getElementById("buy");

  function onBuy() {
    console.log("購入ボタンが押されました");
  }

  // 追加
  btn.addEventListener("click", onBuy);

  // 削除(同じイベント名・同じ関数参照)
  btn.removeEventListener("click", onBuy);
</script>
HTML
  • 削除の照合条件: イベント名・関数参照・第3引数のオプションが追加時と一致している必要があります。
  • 匿名関数は外せない: 追加時に無名関数を渡すと同じ参照を再指定できないため、外せません。変数や名前付き関数で参照を保持しましょう。

よく使うテンプレート集

条件を満たしたら自分で解除(1回だけ動かす)

function onOnce(e) {
  console.log("一度だけ実行");
  e.currentTarget.removeEventListener("click", onOnce);
}
el.addEventListener("click", onOnce);
JavaScript
  • ラベル: 一度だけ処理したいなら自前で解除。オプション { once: true } でも自動解除できます。

追加オプションを合わせて削除(capture など)

function onClick(e) { /* ... */ }
el.addEventListener("click", onClick, { capture: true });
// 削除時も capture: true を一致させる
el.removeEventListener("click", onClick, { capture: true });
JavaScript
  • ラベル: capture の有無が違うと同一リスナーと見なされず、削除できません。

画面遷移・Unmount 時のクリーンアップ

function onScroll() { /* ... */ }
window.addEventListener("scroll", onScroll);

function cleanup() {
  window.removeEventListener("scroll", onScroll);
}
JavaScript
  • ラベル: 古い画面のリスナーが残ると、不要な処理やメモリリークの原因に。必ずクリーンアップ。

委譲(親)でまとめて受けて、不要になったら外す

const list = document.querySelector(".list");
function onListClick(e) {
  const item = e.target.closest(".item");
  if (!item) return;
  console.log("clicked:", item.dataset.id);
}
list.addEventListener("click", onListClick);

// リスト全体のイベントを止めたいとき
list.removeEventListener("click", onListClick);
JavaScript
  • ラベル: 子を個別に外すより、親の一括委譲を止める方が管理が楽。

実務でのコツ

  • 同じ関数参照を渡す: 後から外す予定なら、関数を変数に保持しておく。無名関数は避ける。
  • オプション一致が必須: capture/once/passive は削除時にも一致させる。特に capture の違いで外れない事故が多い。
  • 一度だけなら once を使う: el.addEventListener("click", fn, { once: true }) で自動解除。クリーンアップ漏れを防げます。
  • AbortController でまとめて解除: 追加時に signal を渡すと、controller.abort() 一発で複数のリスナーを解除できます。
const ac = new AbortController();
el.addEventListener("click", fn, { signal: ac.signal });
el.addEventListener("mousemove", fn2, { signal: ac.signal });
// まとめて解除
ac.abort();
JavaScript

ありがちなハマりポイントと対策

  • 関数が違う参照になっている:
    • 対策: 追加時と削除時で同じ関数オブジェクトを使う。必ず変数や名前付き関数で保持。
  • capture を合わせていない:
    • 対策: 追加時の { capture: true/false } を削除時も一致させる。
  • 匿名関数を追加してしまう:
    • 対策: 解除前提なら無名関数は使わない。ラッパーを使うならラッパー自体を変数に保持。
  • クリーンアップ漏れ:
    • 対策: コンポーネント破棄やページ遷移フックで必ず解除。AbortControllerの活用も有効。

練習問題(手を動かして覚える)

<button id="a">A</button>
<button id="b">B</button>
<script>
  const a = document.getElementById("a");
  const b = document.getElementById("b");

  // 1) 追加→3秒後に削除
  function onA() { console.log("A clicked"); }
  a.addEventListener("click", onA);
  setTimeout(() => {
    a.removeEventListener("click", onA);
    console.log("Aのハンドラを解除");
  }, 3000);

  // 2) capture付きで追加→同じ設定で削除
  function onB() { console.log("B capture"); }
  b.addEventListener("click", onB, { capture: true });
  setTimeout(() => b.removeEventListener("click", onB, { capture: true }), 2000);

  // 3) once で自動解除
  b.addEventListener("click", () => console.log("B once"), { once: true });

  // 4) AbortController でまとめて解除
  const ac = new AbortController();
  function onMove(e) { /* ... */ }
  document.addEventListener("mousemove", onMove, { signal: ac.signal });
  setTimeout(() => ac.abort(), 4000); // 4秒後に解除
</script>
HTML

直感的な指針

  • 追加時と同じ「イベント名・関数参照・オプション」で外す。
  • 解除前提のハンドラは必ず参照を保持、匿名関数は使わない。
  • クリーンアップをルーチン化(Unmount/遷移/Abort)して漏れを防ぐ。
タイトルとURLをコピーしました