JavaScript | DOM 操作:イベント発展 – once: true

JavaScript
スポンサーリンク

once: true とは何か

once: true は addEventListener のオプションで、「このリスナーは最初の一回だけ実行し、終わったら自動で取り外す」という宣言です。ここが重要です:removeEventListener を書かなくても、初回実行後に確実に後片付けされるため、二重反応や外し忘れによるバグ、メモリリークを予防できます。初回ガイダンス、初期化、最初のユーザー行動トリガーなどに最適です。


基本の使い方(一度だけ実行して自動解除)

クリックは最初の一回だけ反応させる

<button id="start">はじめる</button>
<script>
  const start = document.getElementById("start");
  start.addEventListener("click", () => {
    console.log("最初の一回だけ実行");
    start.textContent = "準備完了";
  }, { once: true });
</script>
HTML

ここが重要です:2回目以降のクリックでは処理が走りません。リスナーは自動的に外れるため、removeEventListener は不要です。

初回スクロールでヘッダに影を付ける(以降は CSS で維持)

<header id="head" style="position:sticky; top:0">ヘッダ</header>
<script>
  function addShadowOnce() {
    head.classList.add("shadow");
  }
  window.addEventListener("scroll", addShadowOnce, { once: true, passive: true });
</script>
HTML

ここが重要です:初回の「動き出し」だけ JS で反応し、その後は DOM 状態(クラス)で見た目を維持する設計にすると軽く安定します。

DOM 構築完了の一度だけ初期化する

<script>
  document.addEventListener("DOMContentLoaded", () => {
    console.log("初期化");
  }, { once: true });
</script>
HTML

ここが重要です:DOMContentLoaded はそもそも一度しか発火しませんが、once を付けると意図が明確になり、他の再登録コードが混ざっても安全です。


よく使う場面(初回案内・初期化・解析)

初回だけチュートリアル表示

ユーザーがボタンを初めて押した瞬間だけガイドを出し、次回以降は出さない挙動を簡単に作れます。ここが重要です:最初のアクションをトリガーにして「一度きりの UI」を出すと、過剰な案内を避けつつ迷いを減らせます。

初回実行で高コストの初期化

重い計算やモジュール読み込みを、最初の需要が発生した瞬間にだけ行い、その後は済んだ状態で軽く運用できます。ここが重要です:遅延初期化は体感を良くします。once による「一度だけ」の保証があると安全です。

一度きりの解析イベント送信

最初のクリックやスクロールで一度だけログを送る設計に向きます。ここが重要です:過剰な送信を避け、指標の重複計上を防げます。


他オプションとの組み合わせ(capture / passive / signal)

capture と併用する

document.addEventListener("click", (e) => {
  console.log("先取りは一回だけ");
}, { capture: true, once: true });
JavaScript

ここが重要です:キャプチャ段階で「先取りのロジック」を一度だけ走らせたいときに有効です。ただし通常はバブル受けが基本で、キャプチャは例外的な要件に限定します。

passive と併用する

スクロールやタッチなど高頻度イベントでは、passive: true と once: true を併用できます。ここが重要です:初回だけ軽い可視化や準備を行い、その後は処理しないため、応答性と体感が良くなります。preventDefault を使いたい場合は passive を外す必要があります。

AbortController と併用する

const ctrl = new AbortController();
window.addEventListener("scroll", () => { /* 初回だけ */ }, { once: true, passive: true, signal: ctrl.signal });
// 必要に応じて ctrl.abort() で未発火でも一括解除できる
JavaScript

ここが重要です:ページのアンマウントやモーダルクローズ時に、未発火でも安全に後片付けできます。once と signal の組み合わせは、寿命管理が簡潔になります。


落とし穴と注意点(関数参照・複数登録・委譲時の考え方)

同じ要素・同じイベントに複数リスナーを付けると、それぞれ一回ずつ動く

btn.addEventListener("click", A, { once: true });
btn.addEventListener("click", B, { once: true });
JavaScript

ここが重要です:A も B も一度ずつ実行されます。順序や依存関係があるなら、一つのリスナーにまとめるか、明示的な順序制御を設計してください。

無名関数でも問題なく自動解除されるが、手動で外す設計には不向き

once は自動解除するため、無名関数でも構いません。ただし「発火前に条件で外したい」などの手動解除が必要な場合は、関数を変数に保持して使い回せるようにしておきます。ここが重要です:自動解除と手動解除を混在させる場面では、関数参照管理が鍵になります。

委譲(親に1つだけ付けて子の操作に反応)では、once の“適用範囲”を意識する

親に once: true を付けると、最初の“いずれかの子でのイベント”で一度だけ実行され、以降は親のリスナーがなくなります。ここが重要です:委譲で「各子につき一回」を狙うなら、親で一回ではなく、子ごとに状態を持つ設計(クリック済みフラグや属性)に切り替える方が意図に合います。


応用例(初回クリックで設定、初回スクロールで影、初回送信確認)

初回クリックで設定保存し、以降は通常動作

<button id="opt">通知を有効化</button>
<script>
  opt.addEventListener("click", async () => {
    await Promise.resolve(); // 仮の設定保存
    opt.textContent = "有効化済み";
  }, { once: true });

  // 以降、別のリスナーで通常動作(例)
  opt.addEventListener("click", () => console.log("通常クリック"));
</script>
HTML

ここが重要です:初期化用と通常用のリスナーを分け、初期化側だけ once にします。初期化後の体験が安定します。

初回スクロールで進捗バーを表示開始

<div id="bar" style="position:fixed;top:0;left:0;height:3px;background:#09f;width:0;display:none"></div>
<script>
  function startProgress() {
    bar.style.display = "block";
    window.addEventListener("scroll", () => {
      const h = document.documentElement.scrollHeight;
      const vh = window.innerHeight;
      const max = Math.max(h - vh, 1);
      bar.style.width = Math.min(window.scrollY / max, 1) * 100 + "%";
    }, { passive: true });
  }
  window.addEventListener("scroll", startProgress, { once: true, passive: true });
</script>
HTML

ここが重要です:初回だけ「機能を起動」し、以降は通常の軽い監視に切り替えます。段階的起動で無駄を減らせます。

フォーム送信の初回だけ確認、以降はスムーズに送る

<form id="f"><input name="email" required><button>送信</button></form>
<script>
  f.addEventListener("submit", (e) => {
    e.preventDefault();
    if (!confirm("一度だけ確認します。送信しますか?")) return;
    f.requestSubmit(); // 検証を通して送信
  }, { once: true });

// 以降の送信は通常通り(ネイティブ既定動作)
</script>
HTML

ここが重要です:初回だけ確認ダイアログを挟み、意図が固まった後は摩擦なく送れる設計ができます。


まとめ

once: true は「最初の一回だけ実行して自動解除する」リスナーの宣言です。removeEventListener なしで確実に後片付けされ、二重反応や外し忘れを防ぎます。初回ガイダンス、遅延初期化、最初の行動ログなどに向き、capture や passive、signal と組み合わせると寿命管理と性能がさらに良くなります。委譲時は“どの範囲に一回なのか”を意識し、必要なら子単位の状態管理へ。この設計を徹底すれば、初心者でもシンプルで壊れにくいイベント処理を書けます。

タイトルとURLをコピーしました