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


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

## 事象説明
jquery.validate.js を利用して以下の機能を実装していた。

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

画面イメージは以下

![2016-03-07 13.00.28.png](https://qiita-image-store.s3.amazonaws.com/0/115864/609693b5-4e1d-18aa-9afa-d47198721e77.png)

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

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

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

![2016-03-07 13.01.32.png](https://qiita-image-store.s3.amazonaws.com/0/115864/de3bcbcf-0ee6-9952-d01a-72cc5ef365b8.png)

## 結論（修正方法）
先に答えを言ってしまうと、エラーメッセージを `ul>li` で表示するための設定がイケてなかった！

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

```js: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 が呼び出されるわけではないらしく、、、私はこの部分が理解できていなくてハマった

[公式のドキュメント](http://jqueryvalidation.org/validate)には、以下のように書かれている

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

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

では、以下に修正方法を

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

```js: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
});
```

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