タイトル: formのsubmit前にjavascriptを呼び出す方法
SEOタイトル: フォーム submit 前 JavaScript 呼出完全ガイド
| この記事の要点 |
|
submit 前に JS を呼ぶ 3 つの方法
1. onsubmit 属性で関数呼出
<form action="/register" method="post" onsubmit="return validate(event)">
<input name="email" id="email">
<button type="submit">登録</button>
</form>
<script>
function validate(event) {
var email = document.getElementById('email').value;
if (!email.includes('@')) {
alert('メールアドレスが不正です');
return false; // ★ false を返すと送信されない
}
return true; // true なら送信
}
</script>
2. addEventListener(推奨)
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
const email = form.elements['email'].value;
if (!email.includes('@')) {
e.preventDefault(); // ★ デフォルトの送信を中止
alert('メールアドレスが不正です');
return;
}
// 何もしなければそのまま送信される
});
3. ボタン onclick で submit() 呼出
<form id="myForm" action="/register" method="post">
<input name="email" id="email">
<button type="button" onclick="doSubmit()">登録</button>
</form>
<script>
function doSubmit() {
if (!validateEmail()) return;
document.getElementById('myForm').submit();
// ★ form.submit() は submit イベントを発火しない!
}
</script>
3 方法の比較
| 方法 | キーボード送信(Enter) | イベント上書き | 推奨 |
|---|---|---|---|
| onsubmit 属性 + return false | OK | 1 つだけ | レガシー |
| addEventListener('submit') | OK | 複数可 | ★ 推奨 |
| button onclick + submit() | NG(Enter キーで漏れる) | 複数可 | 非推奨 |
preventDefault / return false の違い
// 旧式: return false (onsubmit 属性内で有効)
function onSubmit(e) {
if (!valid()) return false; // 送信中止 + イベント伝播も止める
}
// モダン: event.preventDefault()
form.addEventListener('submit', (e) => {
if (!valid()) {
e.preventDefault(); // 送信のみ中止
e.stopPropagation(); // バブリングも止めるなら
}
});
// jQuery の場合 return false は両方 = preventDefault + stopPropagation
$('form').submit(function(e) {
if (!valid()) return false;
});
HTML5 標準バリデーション
属性だけで基本的なバリデーションが効きます:
<form>
<!-- 必須 -->
<input name="name" required>
<!-- メール形式 -->
<input name="email" type="email" required>
<!-- 数値範囲 -->
<input name="age" type="number" min="0" max="150" required>
<!-- 文字数 -->
<input name="password" minlength="8" maxlength="100" required>
<!-- 正規表現 -->
<input name="zip" pattern="\d{3}-\d{4}" title="例: 100-0001">
<!-- 日付 -->
<input name="birthday" type="date" required>
<button>送信</button>
</form>
未入力で送信すると、ブラウザが標準のエラー表示を出して送信を防ぎます。JS は不要です。
Constraint Validation API
HTML5 のバリデーション状態を JS から細かく制御:
const form = document.querySelector('form');
const email = form.elements['email'];
form.addEventListener('submit', (e) => {
// 全フィールド有効か
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity(); // ブラウザ標準のエラー表示
return;
}
});
// 個別フィールドのチェック
console.log(email.validity);
// {
// valid: false,
// valueMissing: true, // required で空
// typeMismatch: false, // type="email" 形式違反
// patternMismatch: false,
// tooLong: false,
// tooShort: false,
// rangeUnderflow: false,
// rangeOverflow: false,
// stepMismatch: false,
// badInput: false,
// customError: false,
// }
// カスタムエラーメッセージ
email.setCustomValidity('このメールは既に使われています');
email.reportValidity();
// クリア
email.setCustomValidity('');
非同期バリデーション(重複チェック等)
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault(); // ★ 一旦止めて非同期チェック
// HTML5 標準チェック
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// サーバ側で重複チェック
const email = form.elements['email'].value;
const res = await fetch('/api/check-email?email=' + encodeURIComponent(email));
const { exists } = await res.json();
if (exists) {
form.elements['email'].setCustomValidity('既に登録済みです');
form.elements['email'].reportValidity();
return;
}
// OK なら手動で送信
form.submit();
});
React のフォーム例
import { useState } from 'react';
function RegisterForm() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const onSubmit = async (e) => {
e.preventDefault();
if (!email.includes('@')) {
setError('メール形式が不正');
return;
}
const res = await fetch('/api/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ email }),
});
if (!res.ok) setError('登録失敗');
};
return (
<form onSubmit={onSubmit}>
<input value={email} onChange={e => setEmail(e.target.value)} />
{error && <p>{error}</p>}
<button>登録</button>
</form>
);
}
Vue 3 のフォーム例
<script setup>
import { ref } from 'vue';
const email = ref('');
const error = ref('');
async function onSubmit() {
if (!email.value.includes('@')) {
error.value = 'メール形式が不正';
return;
}
await fetch('/api/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ email: email.value }),
});
}
</script>
<template>
<form @submit.prevent="onSubmit">
<input v-model="email">
<p v-if="error">{{ error }}</p>
<button>登録</button>
</form>
</template>
スキーマバリデーション (Yup / Zod / Joi)
// Yup
import * as yup from 'yup';
const schema = yup.object({
email: yup.string().email().required(),
age: yup.number().min(0).max(150).required(),
});
try {
await schema.validate(formData);
} catch (err) {
console.error(err.errors);
}
// Zod
import { z } from 'zod';
const Schema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150),
});
const result = Schema.safeParse(formData);
if (!result.success) console.error(result.error);
FAQ
Q: form.submit() で onsubmit イベントが発火しない
A: 仕様です。プログラムから form.submit() を呼ぶと submit イベントはスキップされます。バリデーション後の送信は新しく form.requestSubmit() (モダン環境) を使うと submit イベントも発火します。
Q: Enter キーで送信されてしまう
A: 仕様。submit イベントは Enter でも発火するので addEventListener('submit') なら漏れません。input の Enter を完全に止めたいなら keydown で e.key === 'Enter' && e.preventDefault()。
Q: 二重送信を防ぎたい
A: submit ボタンを btn.disabled = true に。あるいは Vue/React で送信中フラグを管理。