今回のトピックス
- jquery.validate.js のエラーメッセージ表示制御はあまりに独自実装すると、変な動きするよ
- ちゃんとリファレンス読んでから実装しろよ
- 既存案件で動いているからといって鵜呑みにしたらあかんよ
事象説明
jquery.validate.js を利用して以下の機能を実装していた。
- 住所の入力項目が都道府県、市区町村、町域の3つに分かれている
- 住所全体は入力任意だが、いづれかの項目が入力されている場合は、他の項目が全て必須になる
- エラーメッセージは入力項目の上に、
ul>li
で表示する
画面イメージは以下
ずいぶん前に実装していて、動いているつもりだったが、以下の手順で操作すると
エラーメッセージのボックス部分(ulタグ)だけが残って見た目が不細工になっていた。
- 都道府県を選択し、submit ボタン押下
→ 市区町村、町域のエラーが表示される - 都道府県を未選択に戻す
→ エラーが表示されたまま - 市区町村にフォーカスし、何も入力せずにフォーカスアウトする
→ エラーメッセージは消えるが、枠だけ残ってしまう
事象再現状態の画面イメージが以下
結論(修正方法)
先に答えを言ってしまうと、エラーメッセージを ul>li
で表示するための設定がイケてなかった!
元々の実装(ダメな例)
元々の実装では、errorElement に li
を指定し、ul
タグは errorPlacement の中で独自に生成して表示していた
そのため、validate 結果が正常だった場合に success で ul
タグの子要素が無くなった場合のみ ul
タグを削除する処理をしていた
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 の要素の表示状態を切り替えてくれる処理が実行されるので、事象が改善される
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
});
まとめ
まぁ、ちゃんとリファレンス読んで作れよ、ってことなのですが、、、
私が実装し始めた時からこの部分はこういう実装だったので、正しいと思って進めていたのだダメだったようです、、、
人の実装でも、引き継いだ時にある程度正しいかどうか確認することも必要、ということですね、、、