はじめに
前回記事のボタンを押してからバリデーション実施では、大量のエラーがどっと出ると一つ一つ直していくのは大変です。リアルタイムバリデーションなら、入力中にすぐ「何を直せばよいか」を示すことができます。
今回は、リアルタイムバリデーションの実装方法をLogとして記載していきたいと思います。
他にも、体系的にJavaScriptを学びたい方には以下の教材がおすすめです:
👉1冊ですべて身につくJavaScript入門講座(Amason)
👉 スラスラわかるJavaScript(Amazon)
リアルタイムバリデーションとは
リアルタイムバリデーションとは、ユーザーが入力している最中(キー入力・貼り付け・フォーカス移動など)に即時でチェックを走らせ、エラーやヒントを表示する手法です。
メリット
- 即時フィードバックが可能:送信前に間違いに気づくことができる
- 修正コストの軽減:どの条件に引っかかったかがすぐわかり修正できる
デメリット
- 過剰なアラートは逆効果:入力途中の「未完成」を即エラー扱いしない工夫が必要となる(デバウンスや初回はblurで判定などの工夫)
- パフォーマンス低下:高頻度の検証は負荷に注意(正規表現や外部API照会はデバウンス/スロットリング)
リアルタイムバリデーションの実装方法
htmlとJavaScriptを使用して、簡単な実装例を試してみます。
html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>リアルタイムバリデーション</title>
    <link rel="stylesheet" href="css/realvalid.css" />
    <script src="js/realvalid.js" defer></script>
</head>
<body>
    <form id="signupForm" novalidate>
        <div class="field">
            <label for="username">ユーザー名(3文字以上)</label>
            <input id="username" name="username" type="text" required />
            <p id="usernameError" class="error" aria-live="polite"></p>
        </div>
        <div class="field">
            <label for="email">メールアドレス</label>
            <input id="email" name="email" type="email" required />
            <p id="emailError" class="error" aria-live="polite"></p>
        </div>
        <div class="field">
            <label for="age">年齢(0〜120)</label>
            <input id="age" name="age" type="number" inputmode="numeric" min="0" max="120" required />
            <p id="ageError" class="error" aria-live="polite"></p>
        </div>
        <div class="field checkbox">
            <label>
                <input id="tos" name="tos" type="checkbox" required />
                利用規約に同意する
            </label>
            <p id="tosError" class="error" aria-live="polite"></p>
        </div>
        <button type="submit" id="submitBtn">登録する</button>
        <p id="formStatus" class="status" aria-live="polite"></p>
    </form>
</body>
</html>
css
body {
    font-family: system-ui, sans-serif;
    line-height: 1.6;
    padding: 24px;
}
.field {
    margin-bottom: 16px;
}
label {
    display: block;
    font-weight: 600;
    margin-bottom: 6px;
}
input[type="text"],
input[type="email"],
input[type="number"] {
    width: 320px;
    max-width: 100%;
    padding: 10px;
    border: 1px solid #bbb;
    border-radius: 6px;
    outline: none;
}
input:focus {
    border-color: #4b8cf7;
    box-shadow: 0 0 0 3px rgba(75, 140, 247, .2);
}
.error {
    color: #c83532;
    font-size: 12px;
    min-height: 1.2em;
    margin: 6px 0 0;
}
.is-invalid {
    border-color: #c83532 !important;
    background: #fff7f7;
}
.checkbox label {
    display: inline-flex;
    gap: 8px;
    align-items: center;
    font-weight: 400;
}
button {
    padding: 10px 16px;
    border: none;
    border-radius: 6px;
    background: #0ea5e9;
    color: #fff;
    cursor: pointer;
}
button:disabled {
    opacity: .6;
    cursor: not-allowed;
}
.status {
    margin-top: 12px;
}
JavaScript
// --- デバウンス ---
const debounce = (fn, delay = 200) => {
    let t; return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), delay); };
};
const fields = {
    username: { el: document.getElementById("username"), err: document.getElementById("usernameError") },
    email: { el: document.getElementById("email"), err: document.getElementById("emailError") },
    age: { el: document.getElementById("age"), err: document.getElementById("ageError") },
    tos: { el: document.getElementById("tos"), err: document.getElementById("tosError") },
};
// --- ルール ---
const emailPattern = /^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i;
// --- 検証関数 ---
function validateUsername(value) {
    if (!value.trim()) return "ユーザー名は必須です";
    if (value.trim().length < 3) return "ユーザー名は3文字以上で入力してください";
    return "";
}
function validateEmail(value) {
    if (!value.trim()) return "メールアドレスは必須です";
    if (!emailPattern.test(value.trim())) return "メールアドレスの形式が正しくありません";
    return "";
}
function validateAge(value) {
    if (value === "" || value === null) return "年齢は必須です";
    const n = Number(value);
    if (!Number.isFinite(n)) return "年齢は数値で入力してください";
    if (n < 0 || n > 120) return "年齢は0〜120の範囲で入力してください";
    return "";
}
function validateTos(checked) {
    return checked ? "" : "利用規約に同意してください";
}
// --- 表示更新 ---
function renderFieldState(field, message) {
    field.err.textContent = message || "";
    field.el.classList.toggle("is-invalid", Boolean(message));
}
// --- 単体検証 ---
function validateField(name) {
    switch (name) {
        case "username":
            return renderFieldState(fields.username, validateUsername(fields.username.el.value));
        case "email":
            return renderFieldState(fields.email, validateEmail(fields.email.el.value));
        case "age":
            return renderFieldState(fields.age, validateAge(fields.age.el.value));
        case "tos":
            return renderFieldState(fields.tos, validateTos(fields.tos.el.checked));
    }
}
// --- イベント登録 ---
const onInput = debounce((e) => validateField(e.target.id), 180);
["username", "email", "age"].forEach(id => {
    fields[id].el.addEventListener("input", onInput);
    fields[id].el.addEventListener("blur", (e) => validateField(e.target.id));
});
fields.tos.el.addEventListener("change", () => validateField("tos"));
まとめ
リアルタイムバリデーションは、エラーをその場で直すことができるためユーザビリティの向上につながります。
本記事のテンプレート(HTML/CSS/JS)をベースに、検証ルール・メッセージ・見た目をプロジェクト要件に合わせて調整してみてください。
最後に
JavaScriptの環境構築は、この記事を参照してみてください。
【JavaScript】VSCodeでJavaScriptを使用するための環境構築を実施する – SEもりのLog JavaScript
以上、ログになります。
これからも継続していきましょう!!

 
  
  
  
  
コメント