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

JavaScript
スポンサーリンク

submit とは何か

submit は「フォームが送信される瞬間」に発火するイベントです。Enter キーや type=”submit” のボタンで送信しようとしたとき、フォーム要素で受け取れます。ここが重要です:submit はフォームに付ける。ボタンや input に付けても期待通り動きません。既定動作はブラウザがページ遷移・送信を行うことなので、アプリ側で制御したいときは preventDefault を使います。


基本の使い方(送信を拾う → 既定動作を止める → 処理する)

最小の例(値を読んで送信を止める)

<form id="f">
  <input name="email" type="email" required>
  <button type="submit">送信</button>
</form>
<script>
  const form = document.getElementById("f");
  form.addEventListener("submit", (e) => {
    e.preventDefault(); // 既定のページ遷移を止める
    const data = new FormData(form);      // フォームの値を安全に取得
    console.log("email:", data.get("email"));
  });
</script>
HTML

ここが重要です:FormData は name 属性に基づいて値を集めます。DOMを直接辿るより安全・簡潔です。ファイル入力もそのまま扱えます。

非同期送信(fetch)

<form id="login">
  <input name="user" required>
  <input name="pass" type="password" required>
  <button>ログイン</button>
</form>
<script>
  login.addEventListener("submit", async (e) => {
    e.preventDefault();
    const data = new FormData(login);
    const btn = login.querySelector("button");
    btn.disabled = true; btn.textContent = "送信中…";
    try {
      const res = await fetch("/api/login", { method: "POST", body: data });
      if (!res.ok) throw new Error("失敗");
      console.log("成功");
    } catch (err) {
      console.log(err.message);
    } finally {
      btn.disabled = false; btn.textContent = "ログイン";
    }
  });
</script>
HTML

ここが重要です:視覚(文言)と操作不可(disabled)を同じロジックで同期すると、状態のズレが起きません。エラーでも finally で必ず元に戻します。


既定動作とバリデーション(止める/通すの判断)

ブラウザの制約検証を活かす(required, type, pattern)

<form id="f2">
  <input name="email" type="email" required>
  <button>送信</button>
</form>
<script>
  f2.addEventListener("submit", (e) => {
    if (!f2.checkValidity()) {      // すべての制約を満たすか
      e.preventDefault();           // 無効なら送信を止める
      f2.reportValidity();          // どこが無効かネイティブ表示
      return;
    }
    // 有効なら既定の送信に任せる or 手動送信へ
  });
</script>
HTML

ここが重要です:HTML の制約(required/type/pattern/min/max)をまず活用。無効なら preventDefault して reportValidity で即座にユーザーへ知らせます。

手動送信の差分(requestSubmit と submit の違い)

// 既定の submit イベントと検証を通す
form.requestSubmit(); // → submit イベントが発火、制約検証も走る

// イベントも検証もバイパスして送信(特殊用途)
form.submit();        // → ほぼ使わない。検証・ハンドラを飛ばすため危険
JavaScript

ここが重要です:通常は requestSubmit を使う。form.submit は検証や submit ハンドラを飛び越えるため、意図せず不正な送信を許してしまいます。


複数の送信ボタン(どれで送られたかを判定する)

event.submitter で押されたボタンを知る

<form id="post">
  <input name="title" required>
  <button name="action" value="save">保存</button>
  <button name="action" value="publish">公開</button>
</form>
<script>
  post.addEventListener("submit", (e) => {
    e.preventDefault();
    const which = e.submitter?.value;                 // "save" or "publish"
    const data = new FormData(post);
    data.append("action", which);                      // 送信内容に含める
    console.log("押されたボタン:", which);
    // which に応じて処理分岐
  });
</script>
HTML

ここが重要です:複数ボタンを使う場合、name/value と event.submitter を組み合わせるとシンプルに分岐できます。古いブラウザ向けにはフォールバック(クリック時に状態保持)を検討します。


キーボードとアクセシビリティ(Enter とフォーカスの挙動)

Enter の既定挙動を理解して扱う

  • Enter で送信: テキスト入力がフォーム内でフォーカス中なら Enter で submit が発火します。
  • 複数入力がある場合: どの入力がフォーカスでも送信されます。予期せぬ送信を避けたい場面ではボタンに type=”button” を使うか、submit ハンドラで条件分岐します。
  • 補助: Enter を検索トリガにしたいが送信はしたくない場合、submit を preventDefault し、独自処理へ切り替えます。

ここが重要です:submit は“フォーム全体の意志”。個別キー処理は keydown で、送信は submit で受けると整理しやすいです。


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

  • フォーム以外にリスナーを付ける:
    対策: submit は必ず form に付ける。document に付けるとスコープが広すぎます。
  • preventDefault の乱用:
    対策: ブラウザ既定送信が欲しい場面(サーバーレンダリング)では止めない。SPA のときのみ止める。
  • 手動 submit の誤用:
    対策: form.submit は使わず requestSubmit を使う。検証とイベントを通すのが安全。
  • 値の取得を DOM 直参照でバラバラに:
    対策: FormData を基本に。name 属性を正しく付けると取得が簡潔でミスが減ります。
  • 非同期処理中の二重送信:
    対策: 送信開始でボタンを disabled、完了・失敗で必ず復帰。状態の単一情報源を守る。

実践例(確認ダイアログ、ステップ送信、ファイルアップロード)

送信前に確認してから実行

<form id="del">
  <input name="id" value="42" hidden>
  <button>削除</button>
</form>
<script>
  del.addEventListener("submit", async (e) => {
    e.preventDefault();
    if (!confirm("本当に削除しますか?")) return;
    const data = new FormData(del);
    await fetch("/api/delete", { method: "POST", body: data });
    console.log("削除完了");
  });
</script>
HTML

ステップフォーム(各ステップで検証して前進)

<form id="step">
  <input name="email" type="email" required>
  <button>次へ</button>
</form>
<script>
  step.addEventListener("submit", (e) => {
    e.preventDefault();
    if (!step.checkValidity()) { step.reportValidity(); return; }
    // 次のステップの UI を表示
    console.log("ステップ1 OK");
  });
</script>
HTML

ファイルアップロード(FormData でそのまま送る)

<form id="up">
  <input name="photo" type="file" accept="image/*" required>
  <button>アップロード</button>
</form>
<script>
  up.addEventListener("submit", async (e) => {
    e.preventDefault();
    const data = new FormData(up);             // file を自動で含む
    const res = await fetch("/api/upload", { method: "POST", body: data });
    console.log("status:", res.status);
  });
</script>
HTML

ここが重要です:ファイルは自前で読み出さずとも FormData に含まれます。大きいファイルなら進捗表示やキャンセル(AbortController)も併用します。


まとめ

submit は「フォーム送信の合図」。フォームにリスナーを付け、必要なら preventDefault で既定動作を止め、FormData で値を取得して非同期処理へ渡す。制約検証は checkValidity/reportValidity を活用し、手動送信は requestSubmit を使う。複数ボタンは event.submitter+name/value で分岐、Enter の既定挙動を理解してアクセシブルに設計する。状態は単一情報源で同期し、二重送信や誤送信を防げば、初心者でも堅牢で使いやすいフォーム送信が実装できます。

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