この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:5
ページ更新者:T
更新日時:2026-06-11 07:10:02

タイトル: formのsubmit前にjavascriptを呼び出す方法
SEOタイトル: フォーム submit 前 JavaScript 呼出完全ガイド

この記事の要点
  • 定番: <form onsubmit="return validate(event)"> + return false で送信中止
  • モダン: form.addEventListener("submit", e => { e.preventDefault(); ... })
  • HTML5 標準: required / pattern / type="email" + form.checkValidity()
  • 非同期チェック(重複確認等)は e.preventDefault() → fetch → 成功時 form.submit()
  • Vue / React / Yup / Joi 等のフレームワークに任せるのが最終形

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 falseOK1 つだけレガシー
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 を完全に止めたいなら keydowne.key === 'Enter' && e.preventDefault()

Q: 二重送信を防ぎたい
A: submit ボタンを btn.disabled = true に。あるいは Vue/React で送信中フラグを管理。