JavaScript | DOM 操作:イベント基礎 – イベントのデフォルト動作

JavaScript JavaScript
スポンサーリンク

イベントのデフォルト動作とは何か

「イベントのデフォルト動作」は、ブラウザが標準で行う振る舞いのことです。例として、リンクのクリックでページ遷移、フォームの submit で送信、テキスト入力で Enter が送信を引き起こす、マウスホイールでスクロールなどがあります。ここが重要です:デフォルト動作を理解すると、必要な場面だけ preventDefault で止め、不要な場面では素直にブラウザに任せる設計ができます。アクセシビリティと体感の両方を保つ基礎になります。


デフォルト動作を止める基本(preventDefault とキャンセル可能性)

preventDefault の使い方と e.cancelable

<a id="link" href="/next">次へ</a>
<script>
  link.addEventListener("click", (e) => {
    if (!e.cancelable) return;      // キャンセル可能か確認(保険)
    e.preventDefault();             // 既定の遷移を止める
    history.pushState({}, "", "/next"); // SPA で内部遷移
  });
</script>
HTML

ここが重要です:すべてのイベントがキャンセル可能なわけではありません。e.cancelable が false なら preventDefault は効果がありません。止めたかどうかは e.defaultPrevented で確認できます。

passive リスナーでは preventDefault できない

window.addEventListener("wheel", (e) => {
  // e.preventDefault(); // passive: true だとエラー
}, { passive: true }); // スクロール最適化の宣言
JavaScript

ここが重要です:passive: true は「このハンドラではスクロールを止めない」という約束です。スクロール抑止が必要なら passive を外し、最小限の場面で preventDefault を使います。

preventDefault と stopPropagation の違い

preventDefault は「既定動作」を止めます。stopPropagation は「外側へのイベント伝播」を止めます。ここが重要です:“遷移・送信・スクロールを止めたい”なら preventDefault、“親のクリックハンドラを発火させたくない”なら stopPropagation。役割が全く異なるため混同しないでください。


代表的なデフォルト動作と制御の実例

リンクのクリック(遷移)

<a id="go" href="/next">次へ</a>
<script>
  go.addEventListener("click", (e) => {
    e.preventDefault();           // SPA で制御
    go.textContent = "移動中…";
    history.pushState({}, "", "/next");
  });
</script>
HTML

ここが重要です:アプリ側でルーティングする SPA では遷移を止めます。一方で通常のサーバーレンダリングでは止めず、ブラウザに任せるのが合理的です。

フォームの送信(submit)

<form id="f">
  <input name="email" type="email" required>
  <button type="submit">送信</button>
</form>
<script>
  f.addEventListener("submit", (e) => {
    if (!f.checkValidity()) { e.preventDefault(); f.reportValidity(); return; }
    e.preventDefault(); // 非同期送信へ切り替え
    const data = new FormData(f);
    fetch("/api", { method: "POST", body: data });
  });
</script>
HTML

ここが重要です:ブラウザの制約検証(required/type/pattern)をまず使い、非同期送信のときだけ既定の遷移を止めます。

キーの既定動作(Enter・Backspace・Space)

<input id="q" placeholder="検索">
<script>
  q.addEventListener("keydown", (e) => {
    if (e.key === "Enter") { e.preventDefault(); console.log("検索:", q.value.trim()); }
  });

  document.addEventListener("keydown", (e) => {
    // フォーカスが入力要素でない時の Backspace の“戻る”を抑止(必要な場合のみ)
    const inInput = /^(INPUT|TEXTAREA)$/.test(document.activeElement.tagName);
    if (!inInput && e.key === "Backspace") e.preventDefault();
  });
</script>
HTML

ここが重要です:Backspace の「前ページへ戻る」、Space の「スクロール」などは文脈次第で不快になり得ます。入力時以外は抑止したい場面でだけ止めます。

マウスホイールとタッチのスクロール

const panel = document.getElementById("panel");
panel.addEventListener("wheel", (e) => {
  if (shouldLockScroll(e)) { e.preventDefault(); } // 独自条件
}, { passive: false });
JavaScript

ここが重要です:スクロール抑止は UX を損ねやすいので、モーダル中や水平スクロール専用領域など“明確な必要性”に限定します。

右クリックのコンテキストメニュー

<div id="area">右クリックでメニュー</div>
<script>
  area.addEventListener("contextmenu", (e) => {
    e.preventDefault(); // 既定メニューを止める
    // 独自メニューを表示
  });
</script>
HTML

ここが重要です:独自メニューを出したいときだけ止めます。常時禁止は不便になりがちです。

ドラッグ&ドロップのデフォルト(画像のドラッグなど)

<div id="drop">ここにファイルをドロップ</div>
<script>
  ["dragenter","dragover"].forEach(type => {
    drop.addEventListener(type, (e) => { e.preventDefault(); }); // 受け入れ可能にする
  });
  drop.addEventListener("drop", (e) => {
    e.preventDefault();
    const files = e.dataTransfer.files;
    console.log("受信:", files.length, "件");
  });
</script>
HTML

ここが重要です:dragover/drop を受け付けるにはデフォルト抑止が必須です。止めないとブラウザが“開く”などの既定処理をしてしまいます。

テキスト選択・ドラッグの抑止(必要最小限で)

.nodrag { user-select: none; }
JavaScript

ここが重要です:JS の preventDefault で細かく止めるより、CSS で user-select を使う方が安全でアクセシブルです。ドラッグ抑止は UI 意図が明確な場面に限定します。


設計の指針(いつ止め、いつ通すか)

止める基準を明確にする

「SPA でのナビゲーション」「非同期送信に切り替える」「ドラッグ&ドロップ受け入れ」「独自コンテキストメニュー」など、目的がある場面だけ止めます。ここが重要です:必要性が薄い抑止はユーザーの期待を裏切り、アクセシビリティを損ねます。

アクセシビリティを優先する

ボタンはネイティブ button を使い、Enter/Space の既定動作を活かせる構造にします。ここが重要です:div にクリックを付けてデフォルトを無理に止めるより、適切な要素選択で“止める必要のない UI”を作る方が質が高いです。

最小の範囲で抑止する

document 全体で一律に止めるのではなく、対象要素でだけ止めます。ここが重要です:スコープを絞るほど副作用が減ります。


よくある落とし穴と対策

preventDefault の乱用

「なんとなく全部止める」は体験を悪化させます。必要なイベントだけ、必要な場面だけ止める方針にします。ここが重要です:まず“通す”設計を考え、それでも難しい場所に限って抑止します。

passive と矛盾する設定

スクロールを止めたいのに { passive: true } にしていると preventDefault できません。ここが重要です:止めたい箇所は { passive: false }、それ以外のスクロール監視は { passive: true } を使い分けます。

stopPropagation との取り違え

伝播停止では遷移・送信・スクロールは止まりません。ここが重要です:デフォルト動作を止めたいときは必ず preventDefault。両者の役割を混同しないでください。

キャンセル不可イベントへの誤用

e.cancelable が false のイベントは止められません。ここが重要です:仕様上止められないものは設計を変える(CSS、要素選択、UIロジック)方向で対応します。


実践例(SPAリンク、フォーム非同期、スクロールロック、右クリックメニュー)

SPA のリンク制御

<nav id="nav">
  <a href="/home">Home</a>
  <a href="/about">About</a>
</nav>
<script>
  nav.addEventListener("click", (e) => {
    const a = e.target.closest("a");
    if (!a) return;
    e.preventDefault();
    history.pushState({}, "", a.getAttribute("href"));
    // ここで view を差し替え
  });
</script>
HTML

ここが重要です:リンクに個別リスナーではなく、親に委譲して範囲を限定します。必要最小限の抑止で十分です。

非同期 submit(検証活用+状態同期)

<form id="f">
  <input name="email" type="email" required>
  <button type="submit">送信</button>
</form>
<script>
  f.addEventListener("submit", async (e) => {
    if (!f.checkValidity()) { e.preventDefault(); f.reportValidity(); return; }
    e.preventDefault();
    const btn = f.querySelector("button");
    btn.disabled = true; btn.textContent = "送信中…";
    try { await new Promise(r => setTimeout(r, 800)); }
    finally { btn.disabled = false; btn.textContent = "送信"; }
  });
</script>
HTML

ここが重要です:検証はブラウザ機能を活用し、送信中の重複防止は disabled と文言で“単一ロジック”にまとめます。

モーダル中のスクロールロック

<div id="modal" class="open">…</div>
<script>
  function lockScroll(lock) {
    document.body.style.overflow = lock ? "hidden" : "";
  }
  // 開閉時に lockScroll(true/false) を呼ぶ
</script>
HTML

ここが重要です:JS で wheel を止めるより、CSS overflow を切り替える方が副作用が少なく安全です。

右クリック独自メニュー

<div id="area" style="height:100px;border:1px solid #ccc"></div>
<script>
  area.addEventListener("contextmenu", (e) => {
    e.preventDefault();
    // メニューを e.clientX, e.clientY 付近に表示
  });
</script>
HTML

ここが重要です:標準メニューを置き換えるのは“必要な場面だけ”。汎用ページでは標準メニューを活かした方が使いやすいです。


まとめ

イベントのデフォルト動作は、ブラウザが標準で提供する便利機能です。止めるときは preventDefault、伝播停止とは役割が違うことを明確にし、e.cancelable と e.defaultPrevented を理解する。スクロールは passive との整合を取り、フォームは検証を活用して必要時のみ送信を置き換える。リンク遷移、ドラッグ&ドロップ、右クリックなど、代表的な場面で“必要最小限の抑止”と“素直に通す”の境界を設計する。これを徹底すれば、初心者でも体験を損なわずに意図通りのイベント制御が書けます。

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