JavaScript | DOM 操作:イベント基礎 – addEventListener

JavaScript JavaScript
スポンサーリンク

addEventListener とは何か

addEventListener は「イベントが起きたときに実行する処理(リスナー)を登録する」ためのメソッドです。クリック、入力、スクロール、ロードなど、ブラウザやユーザーの動きを“合図”として受け取り、あなたのコードを走らせます。ここが重要です:要素や window に対して「どのイベント名で」「どの関数を」「どんな条件(オプション)」で動かすかを宣言します。宣言型にすることで、コードが読みやすく保守しやすくなります。


基本の使い方(要素+イベント名+関数)

クリックに反応させる

<button id="buy">購入</button>
<script>
  const btn = document.getElementById("buy");
  btn.addEventListener("click", () => {
    btn.disabled = true;
    btn.textContent = "購入済み";
  });
</script>
HTML

短い処理なら無名関数で十分です。ここが重要です:addEventListener(“click”, 関数) の形を体で覚えましょう。これがイベント処理の出発点です。

別関数を定義して登録する(後で外せる)

<input id="q" placeholder="検索">
<script>
  const q = document.getElementById("q");
  function onInput(e) {
    console.log("入力中:", e.target.value);
  }
  q.addEventListener("input", onInput);

  // もう要らなくなったら取り外せる
  // q.removeEventListener("input", onInput);
</script>
HTML

ここが重要です:取り外し(removeEventListener)したい場面では「同じ関数参照」を使う必要があります。無名関数をその場で渡すと“同一参照”がないため外せません。


イベントオブジェクト(e)を使いこなす

どの要素で何が起きたか、詳細が入っている

<form id="f">
  <input id="email" type="email">
  <button>送信</button>
</form>
<script>
  document.getElementById("f").addEventListener("submit", (e) => {
    e.preventDefault();              // 既定の送信を止める
    const input = e.target.email;    // form の中の要素にアクセス
    console.log("送る前の値:", input.value);
  });
</script>
HTML

ここが重要です:e はイベントの“文脈”。preventDefault で既定動作を止め、target/currentTarget で要素を区別し、必要な情報に素早くアクセスします。

target と currentTarget の違い

<ul id="list">
  <li>りんご</li>
  <li>バナナ</li>
</ul>
<script>
  const list = document.getElementById("list");
  list.addEventListener("click", (e) => {
    console.log("クリックされた具体的要素:", e.target);          // 実際のクリック先
    console.log("リスナーが付いている要素:", e.currentTarget);     // UL
  });
</script>
HTML

ここが重要です:イベント委譲(親に付けて子の動きに反応)では、target を使って“どの子が操作されたか”を判定します。currentTarget は“リスナーの所有者”です。


代表的なイベントと使いどころ

入力系(input、change、composition)

<input id="age" type="number">
<script>
  const age = document.getElementById("age");
  age.addEventListener("input", () => console.log("タイプ中:", age.value));
  age.addEventListener("change", () => console.log("確定後:", age.value));
  age.addEventListener("compositionend", () => console.log("IME確定"));
</script>
HTML

ここが重要です:input は“逐次”、change は“確定”。日本語入力は composition 中は未確定なので、精密な検証は compositionend 以降が安全です。

マウス/キーボード(click、keydown、keyup)

<button id="save">保存</button>
<script>
  const save = document.getElementById("save");
  save.addEventListener("click", onSave);
  document.addEventListener("keydown", (e) => {
    if (e.key === "s" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); onSave(); }
  });
  function onSave() { console.log("保存しました"); }
</script>
HTML

ここが重要です:キーボードショートカットは key と修飾キー(ctrlKey、metaKey、shiftKey、altKey)で判定します。既定動作が重なる場合は preventDefault を忘れずに。

画面読み込み(DOMContentLoaded、load)

<script>
  document.addEventListener("DOMContentLoaded", () => {
    // DOM が構築された直後(画像などの読み込み完了は待たない)
  });
  window.addEventListener("load", () => {
    // ページの全リソース読み込み完了後
  });
</script>
HTML

ここが重要です:DOM を操作するだけなら DOMContentLoaded が速くて十分。画像・CSS まで待つ必要がある処理は load を使います。


オプションを使いこなす(once、passive、capture)

一度だけ動かしたいなら once

<button id="once">一度だけ</button>
<script>
  const btn = document.getElementById("once");
  btn.addEventListener("click", () => console.log("最初の一回だけ"), { once: true });
</script>
HTML

ここが重要です:初回だけの案内やチュートリアルに有効。明示的な remove 不要で後始末が楽になります。

スクロール性能を守るなら passive

window.addEventListener("scroll", () => {
  // 軽い処理のみ(preventDefault はしない)
}, { passive: true });
JavaScript

ここが重要です:passive: true は「このリスナーではスクロールを止めない」という宣言。ブラウザが最適化でき、スクロールのカクつきを減らします。

伝播段階の制御(capture)

document.addEventListener("click", (e) => {
  console.log("キャプチャ段階");
}, { capture: true });
JavaScript

ここが重要です:イベントは「キャプチャ → ターゲット → バブリング」と伝播します。特殊な要件で“先に受けたい”ときだけ capture を使い、通常は不要です。


イベント伝播の制御(stopPropagation と preventDefault)

既定動作を止める

<a id="link" href="/next">次へ</a>
<script>
  document.getElementById("link").addEventListener("click", (e) => {
    e.preventDefault(); // ページ遷移を止める
    console.log("SPA 内で処理");
    history.pushState({}, "", "/next");
  });
</script>
HTML

ここが重要です:リンクやフォームの“既定動作”をカスタム処理に置き換えるときの基本が preventDefault。明確な意図がある場合にのみ使用します。

上位へ伝播させない

<div id="outer">
  <button id="inner">クリック</button>
</div>
<script>
  outer.addEventListener("click", () => console.log("外側"));
  inner.addEventListener("click", (e) => {
    e.stopPropagation(); // 外側へ伝わらない
    console.log("内側のみ");
  });
</script>
HTML

ここが重要です:イベント委譲を使っている場面で“特定の場合だけ外側を動かしたくない”ときに使います。乱用はデバッグを難しくします。


イベント委譲(後から増える要素にも効かせる)

親に一つだけリスナーを付けて、子の操作に反応

<ul id="items"></ul>
<script>
  const list = document.getElementById("items");

  list.addEventListener("click", (e) => {
    const btn = e.target.closest(".delete");
    if (!btn) return;
    const row = btn.closest("li");
    row?.remove();
  });

  // 後から追加しても動く
  const li = document.createElement("li");
  li.innerHTML = `りんご <button class="delete">削除</button>`;
  list.appendChild(li);
</script>
HTML

ここが重要です:大量の子に個別リスナーを付けるより、親でまとめて処理する方が軽く、動的追加にも強い。closest を使って“対象の塊”に辿るのがコツです。


よくある落とし穴と回避策

無名関数だと remove できない

「外したいイベント」は必ず“名前付き関数”を渡します。ここが重要です:removeEventListener は“同じ参照”が必要。関数を変数に保持して使い回します。

this ではなく e.currentTarget を使う

アロー関数では this が変わるため、ターゲット要素は e.currentTarget を使う癖をつけると混乱しません。ここが重要です:文脈依存を避けると、コードが安定します。

重い処理を直接書かない

スクロールや入力のリスナーに“重い計算”を直書きすると体感が悪化します。ここが重要です:軽量化(passive)、間引き(debounce/throttle の実装)、フラグメントへのバッチ更新で滑らかに保ちましょう。


実践例(フォーム検証、ショートカット、ロード表示)

入力をトリムしてボタンを有効化

<input id="email" type="email" required>
<button id="next" disabled>次へ</button>
<script>
  const email = document.getElementById("email");
  const next = document.getElementById("next");

  email.addEventListener("input", () => {
    const v = email.value.trim();
    next.disabled = !(v && email.validity.valid);
  });
</script>
HTML

ここが重要です:イベントで“状態を同期”する。視覚(ボタン文言・有効/無効)と検証結果を同じロジックで制御するとズレません。

Ctrl/⌘+S で保存

<script>
  function save() { console.log("保存"); }
  document.addEventListener("keydown", (e) => {
    if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s") {
      e.preventDefault();
      save();
    }
  });
</script>
HTML

ここが重要です:修飾キーと key を組み合わせ、既定の“ブラウザ保存”を止めてアプリ側にハンドオーバーします。

送信ボタンのローディング表示

<button id="send">送信</button>
<script>
  const send = document.getElementById("send");
  send.addEventListener("click", async () => {
    send.disabled = true;
    send.textContent = "送信中…";
    try { await new Promise(r => setTimeout(r, 800)); }
    finally { send.disabled = false; send.textContent = "送信"; }
  });
</script>
HTML

ここが重要です:イベントで開始→終了の“単一情報源”を作り、視覚と操作不可を同時に切り替えます。


まとめ

addEventListener は「イベント名・処理関数・オプション」を宣言して、UI の動きをコードに結びつける基本メソッドです。e(イベントオブジェクト)で文脈を扱い、preventDefault/stopPropagation で動作と伝播を制御。once/passive/capture で細やかなチューニング、removeEventListener で後片付け、委譲で動的要素にも対応する。重要なのは“状態を一つのロジックで同期する”こと。これを身につければ、初心者でも滑らかで壊れにくいインタラクションを作れます。

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