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 と組み合わせると寿命管理と性能がさらに良くなります。委譲時は“どの範囲に一回なのか”を意識し、必要なら子単位の状態管理へ。この設計を徹底すれば、初心者でもシンプルで壊れにくいイベント処理を書けます。

