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 の既定挙動を理解してアクセシブルに設計する。状態は単一情報源で同期し、二重送信や誤送信を防げば、初心者でも堅牢で使いやすいフォーム送信が実装できます。
