JavaScript | DOM 操作:イベント基礎 – イベントオブジェクト

JavaScript
スポンサーリンク

イベントオブジェクトとは何か

イベントオブジェクト(一般に e と書く)は、「何が、どこで、どのように起きたか」の詳細を持つ“出来事の記録”です。addEventListener のコールバックに渡され、操作対象の要素、イベントの種類、発生時刻、修飾キー、座標、既定動作の制御などにアクセスできます。ここが重要です:イベント処理は「要素」ではなく「文脈(e)」を読む。e を使いこなすほど、正確で壊れにくいインタラクションが書けます。


基本プロパティ(type、target、currentTarget、timeStamp)

どのイベントか、どこで起きたか

<ul id="list">
  <li>りんご</li><li>バナナ</li>
</ul>
<script>
  const list = document.getElementById("list");
  list.addEventListener("click", (e) => {
    console.log(e.type);           // "click"(イベントの種類)
    console.log(e.target);         // 実際にクリックされた“最も内側”の要素
    console.log(e.currentTarget);  // リスナーが付いている要素(ここでは UL)
    console.log(e.timeStamp);      // 発生時刻(ms)
  });
</script>
HTML

ここが重要です:イベント委譲では target で“誰が触られたか”を判定、currentTarget は“どこにリスナーがあるか”。両者の違いを常に意識します。

既定動作と伝播の制御(preventDefault/stopPropagation)

<a id="link" href="/next">次へ</a>
<script>
  link.addEventListener("click", (e) => {
    e.preventDefault();          // 既定のページ遷移を止める
    e.stopPropagation();         // 親への伝播を止める(必要な場合のみ)
    history.pushState({}, "", "/next"); // SPA 的な遷移
  });
</script>
HTML

ここが重要です:preventDefault は“ブラウザの標準動作を置き換える”ときだけ使う。stopPropagation は“外側の処理を発火させたくない”ときに限定して使い、乱用を避けます。


入力・編集系の文脈(input、change、composition)

入力途中/確定後/IME合成の違い

<input id="q" placeholder="検索">
<script>
  const q = document.getElementById("q");
  q.addEventListener("input",  (e) => console.log("途中:", e.target.value));
  q.addEventListener("change", (e) => console.log("確定:", e.target.value));
  q.addEventListener("compositionstart", () => console.log("IME開始"));
  q.addEventListener("compositionend",   () => console.log("IME確定"));
</script>
HTML

ここが重要です:日本語入力では composition 中の値は“未確定”。厳密なバリデーションや検索実行は compositionend 以降に行うと誤判定が減ります。

フォーム送信の文脈(submit の e)

<form id="f">
  <input name="email" type="email" required>
  <button>送信</button>
</form>
<script>
  f.addEventListener("submit", (e) => {
    e.preventDefault();             // 既定送信を停止
    const form = e.currentTarget;   // フォームそのもの
    console.log(form.email.value);  // 名前で要素参照
  });
</script>
HTML

ここが重要です:submit では e.currentTarget が“フォーム本体”。ここから安全に値へアクセスします。


マウス・キーボードの詳細(座標・キー・修飾)

マウスイベントの座標とボタン

<div id="box" style="height:100px;border:1px solid #ccc"></div>
<script>
  box.addEventListener("click", (e) => {
    console.log(e.clientX, e.clientY); // ビューポート座標
    console.log(e.pageX,   e.pageY);   // ドキュメント座標
    console.log(e.button);             // 0:左, 1:中, 2:右
  });
</script>
HTML

ここが重要です:ドラッグや描画系では座標系(client/page)を使い分けます。スクロール影響を受けないなら client、受けるなら page が扱いやすいです。

キー入力と修飾キー

document.addEventListener("keydown", (e) => {
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s") {
    e.preventDefault(); // ブラウザ保存を抑止
    console.log("アプリの保存");
  }
});
JavaScript

ここが重要です:ショートカットは e.key と修飾キー(ctrlKey、metaKey、shiftKey、altKey)の組み合わせで判定。既定動作が重なる場合は preventDefault を忘れない。


フォーカス・ポインタ・スクロール(UI 状態の合わせ方)

フォーカスの文脈(focus/blur)

<input id="name">
<script>
  name.addEventListener("focus", (e) => console.log("フォーカスIN:", e.target));
  name.addEventListener("blur",  (e) => console.log("フォーカスOUT:", e.target));
</script>
HTML

ここが重要です:フォーカスイベントはバブリングしないことに注意(focusin/focusoutはバブリングします)。親でまとめて扱うなら focusin/focusout が便利です。

低コストスクロール(passive の前提)

window.addEventListener("scroll", (e) => {
  // 軽い表示更新のみ。preventDefault はしない。
}, { passive: true });
JavaScript

ここが重要です:スクロールは高頻度。passive: true で“スクロールを止めない”宣言をすると、ブラウザが最適化でき体感が軽くなります。


伝播の仕組み(キャプチャ/ターゲット/バブリング)

どの段階で処理されるか

<div id="outer"><button id="inner">押す</button></div>
<script>
  outer.addEventListener("click", () => console.log("バブリング:外"));
  outer.addEventListener("click", () => console.log("キャプチャ:外"), { capture: true });
  inner.addEventListener("click", () => console.log("ターゲット:内"));
</script>
HTML

ここが重要です:イベントは「キャプチャ → ターゲット → バブリング」の順に伝わる。特殊要件で“先に拾いたい”なら capture を使い、通常はバブリングで十分です。


カスタムイベント(アプリ内の合図を自分で作る)

自作の出来事を発火する

<div id="bus"></div>
<script>
  bus.addEventListener("loaded", (e) => console.log("完了:", e.detail));
  const ev = new CustomEvent("loaded", { detail: { count: 42 } });
  bus.dispatchEvent(ev);
</script>
HTML

ここが重要です:CustomEvent の detail に“必要な文脈”を詰めて合図を送る。DOM の仕組み(伝播・委譲)をそのまま使えるため、疎結合な設計に向きます。


実践例(委譲で削除、ドラッグ座標、IME対応検索)

親で受ける委譲と target 判定

<ul id="items"></ul>
<script>
  items.addEventListener("click", (e) => {
    const del = e.target.closest(".delete");
    if (!del) return;
    del.closest("li")?.remove();
  });

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

ここが重要です:target+closest で“どの塊が対象か”を判定。動的追加にも強く、リスナーの数を増やさずに済みます。

マウスドラッグの座標取得

<div id="pad" style="height:120px;border:1px solid #ccc"></div>
<script>
  let drawing = false;
  pad.addEventListener("mousedown", () => drawing = true);
  pad.addEventListener("mouseup",   () => drawing = false);
  pad.addEventListener("mousemove", (e) => {
    if (!drawing) return;
    const x = e.clientX - pad.getBoundingClientRect().left;
    const y = e.clientY - pad.getBoundingClientRect().top;
    console.log(x, y); // パッド内座標
  });
</script>
HTML

ここが重要です:座標は“要素の境界からの差”で正規化して扱う。UI のブレが減り、実装が安定します。

IME対応の検索トリガ

<input id="q" placeholder="検索">
<script>
  let composing = false;
  q.addEventListener("compositionstart", () => composing = true);
  q.addEventListener("compositionend",   () => { composing = false; trigger(); });
  q.addEventListener("input", () => { if (!composing) trigger(); });
  function trigger() { console.log("検索:", q.value.trim()); }
</script>
HTML

ここが重要です:composition 中は“未確定”なので実行を避け、compositionend で確定後に走らせる。日本語入力の現実に即した設計です。


まとめ

イベントオブジェクトは「出来事の文脈」を運ぶ中心的な情報源です。type/target/currentTarget/timeStamp を起点に状況を把握し、preventDefault/stopPropagation で動作と伝播を制御。入力は input/change/composition を使い分け、マウス・キーボードは座標・キー・修飾キーで精密に判定。伝播段階(capture/target/bubbling)を理解し、委譲で規模に強い設計にする。必要なら CustomEvent で合図を作る。e を正しく読む癖をつければ、初心者でも滑らかで壊れにくいイベント処理が書けます。

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