プログラミング

JavaScript でフォームのバリデーションを実装する方法

溶けそうです

こんにちは!夏休みが明けまして、暑くて溶け…溶けそうです。。

今回は多くの駆け出しにとって立ちはだかる壁の一つであろう?バリデーション機能を題材として、

JavaScript でフォームのバリデーションを実装する方法

という内容を中心に書いていきたいと思います。

前回ブログはこちらから↓

バリデーションとは

バリデーション( validation )は直訳すると、「検証、証明、妥当性確認」という意味で、要は入力された値が規定の条件・書式を満たしているかどうかチェックするよ〜

ということです。

今回は、下記のような簡易的なフォームでバリデーションを実装します。

バリデーションを行う条件は、

  • 「お名前」フォームが入力されているかどうか
  • 「パスワード」フォームが半角英数・5文字以上かどうか
  • 「パスワード(再入力)」が「パスワード」と一致しているかどうか

とします。

尚、「送信」ボタンは動きませんので悪しからず。。

ソースコード

コードはサーバーにもアップしてありますので、コチラから動作確認できます↓

動くサンプルはコチラ

ソースですが…すみません。。長いです。。

HTML & JS

<form action="" method="POST">
    <table>
        <tbody>
            <!-- お名前 -->
            <tr>
                <th></th>
                <td><div class="err-msg-name"></div></td>
            </tr>
            <tr>
                <th><label for="name">お名前</label></th>
                <td><input type="text" name="name" id="name" placeholder="ジャバ スクリプ子"></td>
            </tr>
            <!-- パスワード -->
            <tr>
                <th></th>
                <td class="err-msg-pass"></td>
            </tr>
            <tr>
                <th><label for="password">パスワード</label></th>
                <td><input type="password" name="password" id="password" placeholder="5文字以上の英数字"></td>
            </tr>
            <!-- パスワード再入力 -->
            <tr>
                <th></th>
                <td class="err-msg-passre"></td>
            </tr>
            <tr>
                <th><label for="pass-re">パスワード(再入力)</label></th>
                <td><input type="password" name="pass-re" id="pass-re" placeholder="5文字以上の英数字"></td>
            </tr>
            <!-- 送信ボタン -->
            <tr>
                <th></th>
                <td><input type="submit" class="submit"></td>
            </tr>
        </tbody>
    </table>
</form>
window.addEventListener('DOMContentLoaded', () => {

    // 「送信」ボタンの要素を取得
    const submit = document.querySelector('.submit');
    
    // 「送信」ボタンの要素にクリックイベントを設定する
    submit.addEventListener('click', (e) => {
        // デフォルトアクションをキャンセル
        e.preventDefault();

        // 「お名前」入力欄の空欄チェック
        // フォームの要素を取得
        const name = document.querySelector('#name');
        // エラーメッセージを表示させる要素を取得
        const errMsgName = document.querySelector('.err-msg-name');
        if(!name.value){
            // クラスを追加(エラーメッセージを表示する)
            errMsgName.classList.add('form-invalid');
            // エラーメッセージのテキスト
            errMsgName.textContent = 'お名前が入力されていません';
            // クラスを追加(フォームの枠線を赤くする)
            name.classList.add('input-invalid');
            // 後続の処理を止める
            return;
        }else{
            // エラーメッセージのテキストに空文字を代入
            errMsgName.textContent ='';
            // クラスを削除
            name.classList.remove('input-invalid');
        }

        // 「パスワード」入力欄の形式チェック
        const pass = document.querySelector('#password');
        const errMsgPass = document.querySelector('.err-msg-pass');
        // パスワードが5文字以上の半角英数字であるかどうかのチェック
        if(!pass.value.match(/^([a-zA-Z0-9]{5,})$/)){
            errMsgPass.classList.add('form-invalid');
            errMsgPass.textContent = '半角英数字5文字以上で入力してください';
            pass.classList.add('input-invalid');
            return;
        }else{
            errMsgPass.textContent ='';
            pass.classList.remove('input-invalid');
        }

        // 「パスワード」と「パスワード再入力」が一致しているかどうかのチェック
        const passRe = document.querySelector('#pass-re');
        const errMsgPassRe = document.querySelector('.err-msg-passre');
        if(pass.value !== passRe.value){
            errMsgPassRe.classList.add('form-invalid');
            errMsgPassRe.textContent = 'パスワードとパスワード再入力が一致していません';
            passRe.classList.add('input-invalid');
            return;
        }else{
            errMsgPassRe.textContent ='';
            passRe.classList.remove('input-invalid');
        }
    }, false);
}, false);

CSS

body {
    color: #111111;
}

.title {
    width: 500px;
    font-weight: bold;
    margin-top: 40px;
    text-align: center;
}

table {
    margin-top: 20px;
    width: 500px;
}

table td input {
    width: 300px;
    height: 30px;
    border: 1px solid #c0c0c0;
    border-radius: 4px;
    outline: none;
    margin: 4px 0 4px 8px;
    text-indent: 4px;
}

table th {
    text-align: right;
    font-size: 14px;
    font-weight: normal;
}

table td input:focus {
    border: 1px solid #1982C4;
}

.form-invalid {
    color: #f50000;
    font-size: 12px;
    padding-left: 12px;
}

.input-invalid {
    border: 1px solid #f50000;
}

.submit {
    width: 305px;
    height: 30px;
    color: white;
    background-color: #696969;
    border: 1px solid #696969;
    cursor: pointer;
}

実装手順

以下、上記コードの JavaScript の部分について、順を追って実装の詳細を記載します。

1. 「送信」ボタンの要素を取得し、クリックイベントを設定する

「送信」ボタンの要素とは、<input type="submit" class="submit"> の部分です。submit クラスが付与されているので、それを取得しイベントを設定します。

// 「送信」ボタンの要素を取得
const submit = document.querySelector('.submit');

// 「送信」ボタンの要素にクリックイベントを設定する
submit.addEventListener('click', (e) => { }, false);

2. デフォルトアクションをキャンセルする

preventDefault() ってか「 e 」ってなんだよ……

// デフォルトアクションをキャンセル
e.preventDefault();

と私は思ったんですが。。。

一つ一つ見ていきます。

まず「 e 」は event の略なので、event って書いてもいいし、evt でもいいし、e でもいいし、要は分かれば良いです。

preventDefault() はデフォルトアクションをキャンセルするメソッドです。

イベントにはデフォルトの動作があって、今回の場合は、

submit のクリックイベントのデフォルトアクション

 <input type=”submit”> に cilck イベントと設定すると、クリックした際に、actionで指定されたURLへ遷移すると共に、データ送信を行う。

今回はあくまでもバリデーションに重きを置いているので、action 属性を指定していないのですが、

普通はフォーム送信したい場合は、form タグの action 属性にデータ送信先を指定します。

もし preventDefault() を書かなかった場合、上記のデフォルトアクションが動作してしまうので、バリデーションを行うまえに指定した URL に遷移・データ送信されてしまうということですね。

(ここまで書いて、やっと理解した…)

ちなみに、action 属性でよく使われる HTTPメソッドは、getpost などがあります。

post について詳しくは下記記事ご参考まで↓

3. 要素を取得する

バリデーションの処理を行うために必要な要素を取得します。

// フォームの要素を取得
const name = document.querySelector('#name');
// エラーメッセージを表示させる要素を取得
const errMsgName = document.querySelector('.err-msg-name');

4-1. フォームが空欄の場合

4-1-1. スタイルを変更するためのクラスを追加

エラー時に赤文字のエラーメッセージを表示させると共に、フォームの枠線を赤くしたいので、classList.add() でクラスを追加します。

// クラスを追加(エラーメッセージを表示する)
errMsgName.classList.add('form-invalid');
// エラーメッセージのテキスト
errMsgName.textContent = 'お名前が入力されていません';
// クラスを追加(フォームの枠線を赤くする)
name.classList.add('input-invalid');

するとこういう感じにエラーメッセージが表示されます。

4-1-2. 後続の処理を止める

return で後続の処理を止めます。

// 後続の処理を止める
return;

return がないと、こんな感じで次のパスワード入力のバリデーションの処理が走ってしまいます。

4-2. フォームに入力値がある場合

フォームに入力値がある場合は、else 節の中に入ってきます。

4-2-1. エラーメッセージのテキストに空文字を代入

(エラーメッセージが表示されている場合はそれを消さないといけないので、) textContent プロパティにアクセスして、エラーメッセージのテキストに空文字を代入します。

// エラーメッセージのテキストに空文字を代入
errMsgName.textContent ='';

4-2-2. クラスを削除

classList.remove() でフォームの枠線を赤くするためのクラスを削除します。

// クラスを削除
name.classList.remove('input-invalid');

ここまでで思った以上に長くなっちゃった+後続のパスワードの処理は上記の焼き増しみたいな感じなので、説明ははしょります…

余談:querySelector() と getElementById() の違い

querySelector() メソッドは括弧内に記載した CSS のセレクタの書式に一致する文書内の最初の要素( Element )を取得して返します。

今回は各フォームには input 要素に id を付与しているので、例えば「お名前」フォームなら querySelector(‘#name’) と記載して要素を取得していました。

id という一意な値であれば getElementById() の方が処置が速いらしいのですが(人の目では分からないレベル)、

querySelector を使用すると、id 名でも class 名でも要素が取得できるので、例えば HTML の構造が変わって取得したい要素が id → class に変わった際でも括弧内の記述を変更するだけでいいので、その点は楽なのかなぁ…と。

querySelector() メソッドを使って要素を取得する方法

  • id の場合

const hoge = document.querySelector(‘#○○’);

  • class の場合

const fuga = document.querySelector(.○○’);

注意なのは、querySelector は文書内の「最初の要素」を返すので、複数要素がある場合はうまく取得できません。その場合は querySelectorAll() メソッドを使用するかもしくは別のメソッドを使用するかしないといけないですね。

調べたところ他にも色々な違いがありましたが、これだけで記事書けちゃうレベルだと思ったんで、今回は割愛します(汗

最後に

いかがだったでしょうか?

なんか冗長だなぁ…(自分の書き方が悪い?)と思ってしまうんですが、

基本的には要素を取得してクラスを追加したり削除したりしてるだけで単純な部類に入ると思います。

(自分めちゃくちゃ苦労しましたが…)

バリデーションって PHP で書くのが一般的なのかな?と独学時代思っていたんですが、JavaScript で書くこともできるんだなぁ。。

めっちゃ長くなってしまったけど…今回もいろいろと勉強になりました。

分かりやすく書きたいと思うのですが、それがあまりに長くなってしまうと逆効果だな…

ここまでお読みいただきありがとうございました!