【JavaScript】addEventListenerとメモリリーク & removeEventListenerの重要性

【JavaScript】addEventListenerとメモリリーク & removeEventListenerの重要性

本記事では、removeEventListenerが必要となる主な原因であるメモリリークに焦点を当て、その具体的な対策と、イベントリスナーを効率的に管理するためのオプションについて解説します。

JavaScript #Javascript#メモリリーク#EventListener

【JavaScript】addEventListenerとメモリリーク & removeEventListenerの重要性

サムネイル

本記事では、removeEventListenerが必要となる主な原因であるメモリリークに焦点を当て、その具体的な対策と、イベントリスナーを効率的に管理するためのオプションについて解説します。

更新日: 7/9/2025

addEventListenerは、JavaScriptでの処理を実装する際の基本的な機能です。しかし、リスナーは追加するだけでなく、不要になった際にremoveEventListenerで適切に削除することが、アプリケーションの安定性において重要です。

removeEventListenerの必要性

イベントリスナーを追加する基本的なコードはシンプルです。

const button = document.getElementById('myButton');

function handleClick() {
  console.log('ボタンがクリックされました');
}

button.addEventListener('click', handleClick);
ISSUE - 課題

メモリリークの原因

要素をDOMから削除しても、その要素に紐付いたイベントリスナーが解除されていない場合、リスナー関数や関連する変数がメモリ上に残り、メモリリークの原因となります。アプリケーションが複雑化すると、これがパフォーマンス低下につながる可能性があります。

removeEventListenerは、不要になったイベントリスナーを解除し、メモリを適切に解放するために必要です。

button.removeEventListener('click', handleClick);

removeEventListenerが必要な主なケース

1. 動的要素の削除

モーダルウィンドウやUIコンポーネントなど、動的に作成・削除される要素では、DOMからの削除と同時にイベントリスナーの解除が必要です。これは、ReactやVueなどにおけるコンポーネント破棄時のクリーンアップ処理の基本です。

2. 一時的なイベントリスナー

特定の条件下でのみ必要なイベントリスナーは、その条件から外れた際に削除するべきです。例えば、モーダル表示中のキーボードイベント監視などがこれにあたります。

function showModal() {
  const modal = document.getElementById('modal');
  modal.style.display = 'block';

  function handleEscape(event) {
    if (event.key === 'Escape') {
      closeModal();
    }
  }
  
  function closeModal() {
    modal.style.display = 'none';
    document.removeEventListener('keydown', handleEscape);
  }

  document.addEventListener('keydown', handleEscape);
}

addEventListenerのオプション

addEventListenerの第三引数では、イベントリスナーの動作を制御するオプションを指定できます。これらを活用することで、removeEventListenerの管理が簡単になります。

オプション 説明
once trueにすると、リスナーが一度だけ実行された後に自動的に削除される。
passive trueにすると、リスナーがpreventDefault()を呼ばないことをブラウザに伝え、パフォーマンスを向上させる。
capture trueにすると、イベントが「キャプチャフェーズ」で実行される。(デフォルトは「バブリングフェーズ」)
signal AbortControllersignalを渡し、複数のリスナーを一度に中断・削除できる。

once: true

一度だけ実行したいイベントで、手動でremoveEventListenerを呼ぶ手間を省けます。フォームの二重送信防止などに有効です。

const form = document.getElementById('myForm');

form.addEventListener('submit', (event) => {
  event.preventDefault();
  // 送信処理
}, { once: true });

passive: true

スクロールやタッチなど、高頻度で発生するイベントのパフォーマンスを改善します。preventDefault()を呼ばないことをブラウザに伝えることで、ブラウザはリスナーの完了を待たずに画面描画を継続できます。

window.addEventListener('scroll', () => {
  // スクロール処理
}, { passive: true });

signal: AbortController

複数のイベントリスナーをまとめて管理・削除する場合に有効です。AbortControllerのインスタンスを作成し、そのsignalを各リスナーに渡します。

const controller = new AbortController();
const button = document.getElementById('myButton');

button.addEventListener('click', () => { /* ... */ }, { signal: controller.signal });
window.addEventListener('resize', () => { /* ... */ }, { signal: controller.signal });

// controller.abort() を呼び出すと、関連するリスナーがすべて削除される
setTimeout(() => {
  controller.abort();
}, 5000);

クリーンアップ処理が楽になるものの、iframe間でのpostMessageなど、継続的な監視には注意が必要です。

まとめ

動的なアプリケーションにおいて、不要になったイベントリスナーをremoveEventListenerで解除することは、メモリリークを防ぐ上で重要です。

また、oncesignalといったオプションを活用することで、リスナーの解除をより効率的に管理でき、コードの簡潔性とパフォーマンスを向上させることができます。

検索

検索条件に一致する記事が見つかりませんでした