input属性に頼った、どシンプルなフォームバリデーション

ちょっと思い立って、どシンプルなフォームのバリデーションスクリプトをを作りました。
シンプルと言っても、使い方がシンプルなわけではなく、バリデーションのルールをHTMLのデフォルトに頼るというもの。もうレガシーブラウザもほぼ考えなくてよい時期になってきたので、HTMLやCSSのデフォルトの機能を使っていこうと思います。
デモ
https://codepen.io/cumak/embed/KKXZRRo?default-tab=result
仕様の概要
- バリデーションを突破するまではボタンをdisabledにする
- バリデーションルールは、HTMLの仕様による
- バリデーション突破前は背景色を変更する
- エラーメッセージ機能はなし
- セレクタ指定や複数セレクタに対する処理の簡略化のため、jQueryのslimを使用
既存プラグインに頼ったところ
- 郵便番号入れたら住所を表示する → yubinbango.js
※この記事での説明はありません。
手順
1、inputにhtmlの制約の属性をつける
required、pattern、maxなどいろいろいろいろあります。
2、バリデーションかけたいinputにvalid-needクラスをつける(チェックボックス・ラジオボタンなど選択タイプのものは除く)
<input class="valid-need" type="text" required>このクラスがあるものをチェック対象とします。
3、必須にしたいチェックボックス・ラジオボタンは、ラップ要素にvalid-need-ckクラスをつける
<div class="valid-need-ck">
<label><input type="checkbox" name="メニュー" value="イタリアン"> イタリアン</label>
<label><input type="checkbox" name="メニュー" value="和食"> 和食</label>
<label><input type="checkbox" name="メニュー" value="フレンチ"> フレンチ</label>
</div>4、送信ボタンはvalid-submitBtnクラスをつける
<input class="valid-submitBtn" type="submit" value="確 認" disabled />今回は確認ボタンなしのバージョンです。
desabledはなくてもよいですが、読み込み時に一瞬押せる方のスタイルになるちらつきが気になるので、最初からdesabledを設定しておきます。
5、バリデーション突破前のCSSを書く
input:invalid{
background-color: #fff2f2;
}バリデーション突破前なら、input部分が薄いピンクになるようにしています。:invalidは、required, pattern , step, max などの属性で設定された制約条件に値が適合しないフォームコントロールに一致する擬似クラスです。
6、チェックするためのJSを追加する
function removeDisabled() {
const $btn = $('.valid-submitBtn');
const $validNeed = $('.valid-need');
const $validWrapper = $('.valid-need-ck');
if($btn){
btnChange()
$validWrapper.each(function(){
const $checkboxs = $(this).find('input');
$checkboxs.on('change',function(){
btnChange()
})
})
$validNeed.each(function(){
$(this).on('input change',function(){
btnChange()
})
});
}
function checkInputVal(){
let validityArr = new Array($validNeed.length);
$validNeed.each(function(j){
if (!($(this).is(':invalid'))) {
validityArr[j] = true;
}else{
validityArr[j] = false;
}
})
return validityArr.every(e => e === true)
}
function checkCheckBox(){
let checkboxCkArr = [];
$validWrapper.each(function(){
let ckflag = false;
const $checkboxs = $(this).find('input');
$checkboxs.each(function(){
if($(this)[0].checked){
ckflag = true;
}
})
checkboxCkArr.push(ckflag);
})
const result = checkboxCkArr.every(e => e === true)
return result
}
function btnChange(){
const checkInputOk = checkInputVal()
const checkboxOk = checkCheckBox()
if(checkInputOk && checkboxOk){
$btn.prop('disabled',false);
}else{
$btn.prop('disabled',true);
}
}
}JSの説明
イベントの設定
下記は、イベントの設定です。チェックボックスやラジオボタンはchange(ユーザーによる要素の値の変更が確定)の時、それ以外のテキストボックスなどはinput(各要素のvalueが変更されたとき)とchangeの時に発火するようにしています。
$validWrapper.each(function(){
const $checkboxs = $(this).find('input');
$checkboxs.on('change',function(){
btnChange()
})
})
$validNeed.each(function(){
$(this).on('input change',function(){
btnChange()
})
});後者でinputとchangeの両方を指定しているのは、
- changeだけだと、フォーカスが離れた時にボタンが変わるので、入力中にユーザーに誤解を与えかねない
- inputだけだと、郵便番号で自動的に住所が入った場合や履歴で入力された場合に発火しない
という理由があります。
単体inputのチェック
function checkInputVal(){
let validityArr = new Array($validNeed.length);
$validNeed.each(function(j){
if (!($(this).is(':invalid'))) {
validityArr[j] = true;
}else{
validityArr[j] = false;
}
})
return validityArr.every(e => e === true)
}booleanの配列validityArrを作り、:invalidでチェックし、全部trueならtrueを返します。
グループタイプのinputのチェック
下記は、グループタイプのinputをチェックしています。チェックボックスやラジオボタンが一つでもcheckedになっていたらtrueです。
function checkCheckBox(){
let checkboxCkArr = [];
$validWrapper.each(function(){
let ckflag = false;
const $checkboxs = $(this).find('input');
$checkboxs.each(function(){
if($(this)[0].checked){
ckflag = true;
}
})
checkboxCkArr.push(ckflag);
})
const result = checkboxCkArr.every(e => e === true)
return result
}jqueryのeachがつくづく便利だ…。
送信ボタンのdisabledを外す
下記で、チェックした配列が全てtrueであれば、送信ボタンのdisabledを外します。
function btnChange(){
const checkInputOk = checkInputVal()
const checkboxOk = checkCheckBox()
if(checkInputOk && checkboxOk){
$btn.prop('disabled',false);
}else{
$btn.prop('disabled',true);
}
}まとめ
input属性のバリデーションをそのまま使うと、CSSもわりと自由になるのでいいなと思います。
上記のように背景色ピンクにすると、クロームの場合履歴がきいてるときに青に上書きされてしまうので、ボーダーとかにした方がいいかもしれません。
エラーメッセージもこだわると肥大化しそうなので、また必要なときにやりたいと思います。































