LoginSignup
5
12

More than 5 years have passed since last update.

jquery.validate.js のエラーメッセージ表示制御にハマる

Posted at

今回のトピックス

  • jquery.validate.js のエラーメッセージ表示制御はあまりに独自実装すると、変な動きするよ
  • ちゃんとリファレンス読んでから実装しろよ
  • 既存案件で動いているからといって鵜呑みにしたらあかんよ

事象説明

jquery.validate.js を利用して以下の機能を実装していた。

  • 住所の入力項目が都道府県、市区町村、町域の3つに分かれている
  • 住所全体は入力任意だが、いづれかの項目が入力されている場合は、他の項目が全て必須になる
  • エラーメッセージは入力項目の上に、ul>liで表示する

画面イメージは以下

2016-03-07 13.00.28.png

ずいぶん前に実装していて、動いているつもりだったが、以下の手順で操作すると
エラーメッセージのボックス部分(ulタグ)だけが残って見た目が不細工になっていた。

  1. 都道府県を選択し、submit ボタン押下
    → 市区町村、町域のエラーが表示される
  2. 都道府県を未選択に戻す
    → エラーが表示されたまま
  3. 市区町村にフォーカスし、何も入力せずにフォーカスアウトする
    → エラーメッセージは消えるが、枠だけ残ってしまう

事象再現状態の画面イメージが以下

2016-03-07 13.01.32.png

結論(修正方法)

先に答えを言ってしまうと、エラーメッセージを ul>li で表示するための設定がイケてなかった!

元々の実装(ダメな例)

元々の実装では、errorElement に li を指定し、ul タグは errorPlacement の中で独自に生成して表示していた
そのため、validate 結果が正常だった場合に success で ul タグの子要素が無くなった場合のみ ul タグを削除する処理をしていた

validate初期化処理
var validator = $('#input-form').validate({
    errorElement: 'li',
    errorPlacement: function(error, element) {
        var id = $(element).attr('id'),
            $parentTd = $(element).closest('td'),
            $errorBox = $parentTd.find('ul.error');

        if ($errorBox.length === 0) {
            // ### ここで、ul タグを自作している
            $errorBox = $('<ul class="error">').prependTo($parentTd);
        }

        $errorBox.append(error);
    },
    success: function(error, element) {
        var $parentTd = $(element).closest('td'),
            $errorBox = $parentTd.find('ul.error');

        $(error).remove();
        if ($errorBox.children().length === 0) {
            // ### ここで ul タグを削除している
            $errorBox.remove();
        }
    },
    rules: {
        // ### 省略 ###
    },
    messages: {
        // ### 省略 ###
    }
});

しかし、そもそも query.validate.js の仕様的に、エラーメッセージが削除される時に必ず success が呼び出されるわけではないらしく、、、私はこの部分が理解できていなくてハマった

公式のドキュメントには、以下のように書かれている

If specified, the error label is displayed to show a valid element.

拙い訳をすると「指定した場合、エラーラベルが正常な要素であることを表示する」という感じ
ソースを見ていると、rules の指定に従ってチェック処理をし、チェックが正常だった場合に success が実行される模様
そのため、条件付必須のように、チェックが不要だった場合には success が実行されない
でも、エラーメッセージは削除してくれるので、枠だけ置いてけぼりになってしまう

では、以下に修正方法を

修正後の実装(正しい例)

しかし、jquery.validate には wrapper という設定があり、エラーメッセージをここで指定した要素で囲んでくれる機能が用意されている
これを利用すると、success が呼び出されない場合でも wrapper の要素の表示状態を切り替えてくれる処理が実行されるので、事象が改善される

validate初期化処理
var validator,
    errorPlacement = function(error, element) {
        var id = $(element).attr('id'),
            $parentTd = $(element).closest('td');

        // #####
        // error には li 要素ではなく、
        // wrapper に設定した ul 要素が li を包含した状態になっているので、
        // スタイルだけ追加しておく
        // #####
        $(error).addClass('error');
        $parentTd.prepend(error);

    },
    rules = {
        // ### 省略 ###
    },
    messages = {
        // ### 省略 ###
    };


validator = $('#input-form').validate({
    errorElement: 'li',
    wrapper: 'ul',    // ### wrapper の設定を追加
    errorPlacement: errorPlacement,
    // ### successは不要になった
    // success: successValidation,
    rules: rules,
    messages: messages
});

まとめ

まぁ、ちゃんとリファレンス読んで作れよ、ってことなのですが、、、
私が実装し始めた時からこの部分はこういう実装だったので、正しいと思って進めていたのだダメだったようです、、、
人の実装でも、引き継いだ時にある程度正しいかどうか確認することも必要、ということですね、、、

5
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
12