【Javascript】入力フォームバリデーションを実装する -作って学ぶWeb制作-

JavaScript実践練習コース

はじめに

ご覧いただきありがとうございます!作って学ぶWeb制作も今回から上級編になります。
今回は入力フォームのバリデーションチェックを実装していきます。ネットで買い物をする際や何か申し込みをするときなど、入力フォームに値を入力する機会はたくさんありますが、内容が条件を満たしているか、未入力がないか等を確認することは重要です。

この記事では入力項目に合わせたバリデーションを実装し、適切なエラーメッセージの出し方を作りながら学んでいきます。

では今回作る入力フォームのデモと仕様を確認していきましょう。

デモ

このフォームでは、氏名・電話番号(3分割入力)・メールアドレス・性別・利用規約同意を入力・選択し、バリデーションをリアルタイム&入力内容の送信時に行います。
エラーがある場合は、該当項目の下にエラーメッセージが表示されます。

仕様

以下の仕様をもとに実装を進めていきます。

各項目はリアルタイムでバリデーション(入力チェック)を実施

電話番号欄は3つに分かれ、最大文字数入力後は自動的に次の欄へカーソル移動

氏名は未入力、メールは形式不正、電話は桁数不正時にエラーメッセージ表示

性別未選択、利用規約未同意もエラーメッセージ表示

すべて正しく入力された場合、送信完了アラートを表示&フォームリセット

css、jsファイルは外部ファイルとしてhtmlで読み込みます。

 【実践】入力フォームを実装する

HTMLで入力欄とフォーム枠組みを準備する

フォームの入力欄と送信ボタン、エラーメッセージを表示する欄を作っていきます。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>フォームバリデーション サンプル</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<header>
    <h1>作って学ぶWeb制作 上級</h1>
    <h2>フォームバリデーションを実装しよう</h2>
</header>
<!-- main-form -->
<form id="contactForm">
    <div class="form-group">
        <label for="name">氏名 <span class="required">必須</span></label>
        <input type="text" id="name" name="name">
        <div class="input-example">例: 山田 太郎</div>
        <div class="error" id="nameError"></div>
    </div>
    <div class="form-group">
        <label for="phone">電話番号 <span class="required">必須</span></label>
        <div class="phone-group">
            <input type="tel" id="phone1" name="phone1" maxlength="3"> -
            <input type="tel" id="phone2" name="phone2" maxlength="4"> -
            <input type="tel" id="phone3" name="phone3" maxlength="4">
        </div>
        <div class="input-example">例: 090-1234-5678</div>
        <div class="error" id="phoneError"></div>
    </div>
    <div class="form-group">
        <label for="email">メールアドレス <span class="required">必須</span></label>
        <input type="email" id="email" name="email">
        <div class="input-example">例: example@example.com</div>
        <div class="error" id="emailError"></div>
    </div>
    <div class="form-group">
        <label>性別 <span class="required">必須</span></label>
        <div class="radio-group">
            <label><input type="radio" name="gender" value="male"> 男性</label>
            <label><input type="radio" name="gender" value="female"> 女性</label>
            <label><input type="radio" name="gender" value="other"> その他</label>
        </div>
        <div class="error" id="genderError"></div>
    </div>
    <div class="form-group">
        <label>
            <input type="checkbox" id="agree" name="agree">
            利用規約に同意します <span class="required">必須</span>
        </label>
        <div class="error" id="agreeError"></div>
    </div>
    <button type="submit">送信</button>
</form>
<!-- end-main-form -->
<script type="text/javascript" src="script.js"></script>
</body>
</html>

ソースコード ポイント解説

    <div class="form-group">
        <label for="name">氏名 <span class="required">必須</span></label>
        <input type="text" id="name" name="name">
        <div class="input-example">例: 山田 太郎</div>
        <div class="error" id="nameError"></div>
    </div>

フォームの各入力項目を「ラベル+入力欄+エラー表示」のグループとしてまとめています。それが1つのform-groupになっています。
ユーザーがどこで間違えたか分かるエラーメッセージ表示用の<div>を各項目の下に配置しています。

CSSでフォームのデザインを整える

フォームの見た目を整えていきましょう!フォームは“入力すること”が目的なので装飾は控えめで、わかりやすさを重視していきます。

body {
    font-family: 'Segoe UI', sans-serif;
    height: 100vh;
}

header {
    text-align: center;
}

form {
    padding: 30px;
    margin: 0 auto;
    border: 3px solid #2980b9;
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
    max-width: 450px;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 5px;
}

.required {
    display: inline-block;
    background: #e74c3c;
    color: #fff;
    font-size: 0.7em;
    padding: 2px 6px;
    border-radius: 4px;
    margin-left: 8px;
    vertical-align: middle;
}

input[type="text"],
input[type="tel"],
input[type="email"] {
    padding: 10px 15px;
    width: 80%;
    border: 2px solid #ccc;
    border-radius: 8px;
    font-size: 1em;
    transition: border-color 0.3s;
}

input:focus {
    border-color: #66afe9;
    outline: none;
}

.error {
    color: #e74c3c;
    font-size: 0.85em;
    margin-top: 5px;
}

.success {
    border-color: #2ecc71 !important;
}

.invalid {
    border-color: #e74c3c !important;
}

.checkbox-group,
.radio-group {
    margin-top: 8px;
}

button {
    width: 100%;
    padding: 12px;
    background: #87cfff;
    border: none;
    border-radius: 8px;
    font-size: 1em;
    cursor: pointer;
    transition: background 0.2s;
}

button:hover {
    background: #64b0e2;
}

.input-example {
    font-size: 0.9em;
    color: #666;
    margin-top: 5px;
    padding-left: 10px;
}

.phone-group {
    display: flex;
    gap: 5px;
    align-items: center;
}

.phone-group input {
    width: 14%;
}

ソースコード ポイント解説

入力欄のバリデーション結果(OKかエラー)に応じて、枠線の色を変えています。エラーメッセージも表示しますが、枠の色が変わるとより直感的にわかるという効果がありますね。
.success {
    border-color: #2ecc71 !important;
}
.invalid {
    border-color: #e74c3c !important;
}

Javascriptで項目に応じたエラーチェックをする

ここまででフォームの見た目と必要な項目はそろった形になっています。最後に今回のメインとなるエラーチェックを実装していきましょう。

const form = document.getElementById("contactForm");
const fields = {
    name: {
        input: document.getElementById("name"),
        error: document.getElementById("nameError"),
        validate: value => value.trim() !== "",
        message: "氏名を入力してください。"
    },
    phone: {
        input: [
            document.getElementById("phone1"),
            document.getElementById("phone2"),
            document.getElementById("phone3")
        ],
        error: document.getElementById("phoneError"),
        validate: values => values.every(value => /^\d+$/.test(value) && value.length > 0) &&
            (values.join("").length === 10 || values.join("").length === 11),
        message: "正しい電話番号を入力してください(10~11桁の数字)。"
    },
    email: {
        input: document.getElementById("email"),
        error: document.getElementById("emailError"),
        validate: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
        message: "正しいメールアドレスを入力してください。"
    }
};

const validateField = (field) => {
    const inputs = Array.isArray(field.input) ? field.input : [field.input];
    const values = inputs.map(input => input.value.trim());
    let isValid;
    if (inputs.length > 1) {
        // 電話番号のような複数inputなら配列で渡す
        isValid = field.validate(values);
    } else {
        // 普通の入力欄なら文字列を渡す
        isValid = field.validate(values[0]);
    }

    inputs.forEach(input => input.classList.remove("success", "invalid"));
    if (!isValid) {
        field.error.textContent = field.message;
        inputs.forEach(input => input.classList.add("invalid"));
        return false;
    } else {
        field.error.textContent = "";
        inputs.forEach(input => input.classList.add("success"));
        return true;
    }
};

// 入力時リアルタイムバリデーション
Object.entries(fields).forEach(([key, field]) => {
    const inputs = Array.isArray(field.input) ? field.input : [field.input];
    inputs.forEach(input => {
        input.addEventListener("input", () => validateField(field));
    });
});

form.addEventListener("keydown", function (event) {
    if (event.key === "Enter") {
        event.preventDefault();
    }
});

form.addEventListener("submit", function (event) {
    event.preventDefault();
    let valid = true;

    Object.values(fields).forEach(field => {
        if (!validateField(field)) valid = false;
    });

    // チェックボックス
    const agree = document.getElementById("agree");
    const agreeError = document.getElementById("agreeError");
    if (!agree.checked) {
        agreeError.textContent = "利用規約に同意してください。";
        valid = false;
    } else {
        agreeError.textContent = "";
    }

    // ラジオボタン
    const genderChecked = document.querySelector('input[name="gender"]:checked');
    const genderError = document.getElementById("genderError");
    if (!genderChecked) {
        genderError.textContent = "性別を選択してください。";
        valid = false;
    } else {
        genderError.textContent = "";
    }

    if (valid) {
        alert("送信完了しました!");
        form.reset();
        Object.values(fields).forEach(field => {
            const inputs = Array.isArray(field.input) ? field.input : [field.input];
            inputs.forEach(input => input.classList.remove("success"));
        });
    }
});

// --- 電話番号 自動カーソル移動 ---
const phoneInputs = [document.getElementById("phone1"), document.getElementById("phone2"), document.getElementById("phone3")];
phoneInputs.forEach((input, index) => {
    input.addEventListener("input", () => {
        if (input.value.length >= input.maxLength) {
            if (index < phoneInputs.length - 1) {
                phoneInputs[index + 1].focus();
            }
        }
    });
});

ソースコード ポイント解説

各入力欄に対して、リアルタイムでバリデーションをかけている部分は以下になります。

Object.entries(fields).forEach(([key, field]) => {
    const inputs = Array.isArray(field.input) ? field.input : [field.input];
    inputs.forEach(input => {
        input.addEventListener("input", () => validateField(field));
    });
});

fieldsというオブジェクト(#name, #phone1, #phone2など)をまとめたものをループしています。そして、各入力欄(input)に対してinputイベントリスナーを付けています。こうすることで、ユーザーが何か入力するたびに、validateField(field)を呼び出し、リアルタイムに入力内容をバリデーション可能になります。

以下のEnterキーでの誤送信防止もポイントです。

form.addEventListener("keydown", function (event) {
    if (event.key === "Enter") {
        event.preventDefault();
    }
});

フォーム全体(#contactForm)に対してkeydownイベントを仕掛けています。event.key === “Enter”だった場合はevent.preventDefault()で、送信を無効化します。入力中にうっかり送信してしまうことを防ぎます。

preventDefault()…デフォルトのイベント動作をキャンセルするメソッド

最後に、電話番号の入力欄に対する部分を見てみましょう!

const phoneInputs = [document.getElementById("phone1"), document.getElementById("phone2"), document.getElementById("phone3")];
phoneInputs.forEach((input, index) => {
    input.addEventListener("input", () => {
        if (input.value.length >= input.maxLength) {
            if (index < phoneInputs.length - 1) {
                phoneInputs[index + 1].focus();
            }
        }
    });
});

ここでは電話番号の自動フォーカス移動がポイントです。
電話番号の入力フィールド#phone1, #phone2, #phone3に対して、inputイベントを監視して最大文字数入力されたら自動で次の入力欄にフォーカスを移動させています。

どこにエラーがあるのかすぐに分かることや入力ミスをすぐ修正できるようにユーザーの使いやすさを考えることが大切なんだね

まとめ

今回の作って学ぶWeb制作では入力フォームのバリデーションを実装してみました。リアルタイムでのエラーチェックや、ユーザーに優しいインターフェースを意識した設計は、ユーザーのストレスを減らすことにもつながります。入力フォームに限らず、どんな機能も使いやすさを考えて設計・実装することが大切ですね。
今回もお読みいただきありがとうございました。

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