javaScriptでつくる簡易バリデータ(入力検証、入力制限)

2009/4/20: html(2つ目のソース)をちょっと修正

    • -

現在、簡単なアプリケーションを作ろうとしているのだが、クライアントサイドで簡単なバリデーション(入力検証、入力制限)を行う適当なツールが見当たらない。
こちらのサイトGigazineさんの記事)にいくつか載っているのだが、GPL系のはちょっと敬遠しちゃうし、fValidatorはMITライセンスでよさそうだけど、MooToolKitに依存してるし、日本語化しなくちゃいけないので、いくぶん面倒くさい。

いずれにしても、クライアントサイドで行うバリデーションは簡易的なもの。サーバーサイドで、より厳密な入力チェックをしなければならない。

なので、正規表現を使って、簡単に作ってみた。

検証する内容(=仕様)は以下とした。

  1. 入力必須
  2. アルファベットのみ(大文字/小文字、スペース、ピリオド、アンダースコア、バーも可)
  3. 大文字アルファベットのみ(小文字、スペース、ピリオド、アンダースコア、バーも可)
  4. 数字のみ(±入力可、小数点以下の入力も可)
  5. 整数(±入力可)
  6. アルファベットと数字のみ(大文字/小文字、スペース、ピリオド、アンダースコア、バーも可)
  7. 日付(YYYY/MM/DD)の形式のみ可
  8. Emailの形式(大文字/小文字アルファベット、ピリオド、アンダースコア、パーセント記号、バー可)
  9. 電話番号(数字、半角スペース、半角括弧、バーで構成されていれば可)
  10. 郵便番号(123-4567、1234567のいずれの形式でも可)
  11. ひらがな(あ−ん、全角スペース可)
  12. カタカナ(カ−ン、全角スペース可)

まだ、考えられると思うが、クライアントサイドでの検証なので、手っ取り早くやりたい。

それと、

  1. 検証エラーが発生したら、エラーメッセージを返却する
  2. エラーメッセージは、日本語と英語を用意する
  3. 検証するのは、INPUTタグのtype="text"
  4. INPUTタグのclass属性で、検証のタイプを指定する(ようにしたい)

とした。

ソースコードは以下。
(ライセンスはAL2.0としたので、必要に応じて、修正したり、拡張して使っていただいてOKです)

/*!
 * MyValidator
 * 
 * ( project : EzoGP )
 * 
 * 
 * (C) 2009, tetsuya odaka.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * クライアントサイドでの入力検証オブジェクト。
 * 
 *  (クロージャーパターンで実装)
 * 
 * @author
 *     Tetsuya Odaka  ( EzoGP )<br>
 * 
 */
var MyValidator = function() {
    
    /*
     * 検証コンポーネント識別子テーブル(英語)
     * 		外部からアクセス可能なので、必要に応じて利用時に書き換えできます。
     */
    this.valMsg = {
    	isRequired	:"required",
    	isAlpha		:"alphabetic characters only",
    	isCapAlpha	:"capital alphabetic characters only",
        isNum		:"number only.",
        isAlphaNum	:"alphanumeric characters only",
        isDate		:"invalid date format. yyyy/mm/dd only.",
        isEmail		:"invalid e-mail format.",
        isPhone		:"invalid phone format",
        isInteger	:"integer only.",
        isZip		:"invalid zip format. 123-4567 or 1234567.",
        // 以下は、return用のみメッセージ
        falseUse	:"invalid call"
    };

    /*
     * 検証コンポーネント識別子テーブル(日本語)
     * 		外部からアクセス可能なので、必要に応じて利用時に書き換えできます。
     */
    this.valMsgJp = {
    	isRequired	:"入力必須です.",
    	isAlpha		:"アルファベットでなくてはいけません.",
    	isCapAlpha	:"アルファベット(大文字)でなくてはいけません.",
        isNum		:"数値でなくてはいけません.",
        isAlphaNum	:"英数文字でなくてはいけません.",
        isDate		:"日付(yyyy/mm/dd)が正しくありません.",
        isEmail		:"メイルアドレスの形式が正しくありません.",
        isPhone		:"電話番号の形式が正しくありません.",
        isInteger	:"整数でなくてはいけません.",
        isZip		:"郵便番号の形式(123-4567,1234567)が正しくありません.",
        isHiragana	:"ひらがなで入力してください.",
        isKatakana	:"カタカナで入力してください.",
        // 以下は、return用のみメッセージ
        falseUse	:"システムエラー;呼び出しが誤っています."
    };
    
    /*
     * 検証コンポーネント正規表現テーブル
     * 		外部からアクセス可能なので、必要に応じて利用時に書き換えできます。
     */
    this.valRex = {
    	isRequired	:/[^.*]/, // key(isRequired)だけ使う
       	isAlpha		:/^[a-z\s\._-]+$/i,
       	isCapAlpha	:/^[A-Z\s\._-]+$/,
        isNum		:/^[-+]?\d*\.?\d+$/,
        isAlphaNum	:/^[a-z0-9\s\._-]+$/i,
        isDate		:/^\d{4}\/(\d{1}|\d{2})\/(\d{1}|\d{2})/, // yyyy/mm/dd
        isEmail		:/^[a-z0-9\._%-]+@[a-z0-9\.-]+\.[a-z]{2,4}$/i,
        isPhone		:/^[\d\s()-]+$/,
        isInteger	:/^[-+]?\d+$/,
        isZip		:/^\d{3}-?\d{4}$/,
        isHiragana	:/^[あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろをん ]+$/,
        isKatakana	:/^[アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン ]+$/
    };

   /**
     * 検証を実行するメソッド
     * 
     * @param
     *     		_locale;	クライアントのロケール
     *     		_key; 		検証の内容を表すもの。
     *     		_string; 	検証対象。
     * @return
     * 			_rArray;	エラー配列(エラーなしのときは長さ0)
     */
    this.validate = function(_locale,_keys,_string){
    	
    	// inputエレメントのclass属性(スペース区切り)を配列化
    	var _kArray = _keys.split(' ');
    	
    	// localeからメッセージ配列名を生成する。
		var _mArrayNam = this.valMsg;
    	if(_locale.match(/.*(ja|jp).*/i)){
			 _mArrayNam = this.valMsgJp; 
		}
    	
    	var _rArray = new Array();
    	// 配列化できなかったら、「呼び出しエラー」にする.
    	if(_kArray<1){
			_rArray[_key] = _mArrayNam['falseUse']; 
    		// 終了
    		return _rArray;
    	}
    	
    	// 取り出したキーそれぞれについて、検証を行う。
    	for(_i in _kArray){
    		
    		var _key = _kArray[_i];

    		// タブとスペースを取り除く
    		_string.replace(/[\t\s]+$/,'');
    	
    		// isRequiredの場合、未入力のチェック
    		if(_key == "isRequired"){
    			if(_string == null || _string.length < 1){
   	   				_rArray[_key] = _mArrayNam['isRequired']; 
    			}
    		}else{
    			// isRequired以外の場合、正規表現でエラー検出
    			if(!_string.match(this.valRex[_key])){
   					_rArray[_key] = _mArrayNam[_key]; 
    			}
    		}
    	
    		/* 
    		 * 連想配列でチェックするのが面倒なものの処理
    		 */
    		// カレンダーのチェック
    		if(_key == 'isDate'){
    			var _year 	= _string.substr(0, 4);
    			var _month 	= _string.substr(5, 2)-1; 
    			var _day 	= _string.substr(8, 2);
    			// 月,日の妥当性チェック
    			if(_month >= 0 && _month <= 11 && 
    					_day >= 1 && _day <= 31){
    				var _date = new Date(_year, _month, _day);
    				// エラー	
    				if(isNaN(_date)){
       					_rArray[_key] = _mArrayNam[_key];
    				}
   				// エラー	
    			}else{
   					_rArray[_key] = _mArrayNam[_key]; 
    			}
    		}
    	}
    	return _rArray;
    };
    
    /* 自身の参照値を返却 */
    return this;
};


以下が、このオブジェクト(MyValidator)を使ったサンプル画面と、そのソースコード
サンプルの挙動は、

  1. 入力フィールドにガイドを表示。
  2. そこにフォーカスしたイベントでガイドを消去。
  3. フォーカスが離れたタイミングでバリデートを行い、エラーが発生したら入力フィールドをピンクにして、エラーメッセージを(入力フィールド下に)表示。

となる。
バリデートの種類(isRequired、isAlphaなどで指定)は、INPUT要素のclass属性として定義している。
検証は、FireFox3.0、Chrome1.0、Safari3.2、IE7で行った。

DOM操作とEvent操作には、YUI2.7.0を使っている。

<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<META http-equiv="Content-Style-Type" content="text/css">
<TITLE>MyValidate</TITLE>

<style type="text/css">

input.ez_initguide {
    /* gray */
    color:#808080;
}

</style>

<!-- YUI2.7.0のdom-eventを使用 --> 
<script type="text/javascript" src="../scripts/lib/yui/build/yahoo-dom-event/yahoo-dom-event.js">
</script>
<!-- MyValidaorオブジェクトを含むjsファイル --> 
<script type="text/javascript" src="../scripts/myznala.js">
</script> 

<script type="text/javascript">
/*
 * 入力検証
 *  (モジュールパターンで実装)
 */
validate = function() {
	var     valObj;
	var	Dom 	= YAHOO.util.Dom;	
	var	Event 	= YAHOO.util.Event;

	/**
	* Inputにフォーカスが当たったときのハンドラー
	*   _evt; YUI.util.Eventが(自動的に)入る。
        *   _obj; 取得したINPUT要素(HTMLInputElement)。
        */
	var onFocusHdlr = function(_evt,_obj) {

		// 初期値(入力ガイド)の場合だけ、入力値を消す。
		//  Dom.getStyleColor()は
		//    rgb(128,128,128); mozilla, chrome, 
		//    #808080; IE7 
		//  と異なった戻り値となる。
		// _objは,HTMLInputElement;
		
		_e = YAHOO.util.Event.getTarget(_evt)
		var _id = _e.id;
		var _resId = _id + '_res';
		
		var _fColor=Dom.getStyle(_id, 'color');
		if( _fColor == 'rgb(128, 128, 128)' ||
				_fColor == '#808080'){ // for IE
			Dom.get(_id).value="";
		}
		// 入力フィールドの色を白にする。
		Dom.setStyle(_id, 'background-color', 'white');
		// 入力フィールドの文字の色をグレーから黒に変える
		Dom.setStyle(_id, 'color', 'black');
		// メッセージフィールドを消す
		Dom.get(_resId).innerHTML="";
	};

	/**
	* Inputからフォーカスが外れたときのハンドラー
	*   _evt; YUI.util.Eventが(自動的に)入る。
        *   _obj; 取得したINPUT要素(HTMLInputElement)。
	*/
	var onBlurHdlr = function(_evt,_obj){
		
		_e = YAHOO.util.Event.getTarget(_evt)
		var _id = _e.id;
		var _resId = _id + '_res';

		// 検証キーをclass属性から取得.
		if(document.all){
			//for IE
			var _keys = Dom.get(_id).getAttribute('className');
		}else{
			// for FF, Chrome, Safari
			var _keys = Dom.get(_id).getAttribute('class');
		}
		
		// 検証対象を取得.
		var _str  = Dom.get(_id).value;

		// 検証
		var _retArray = valObj.validate(getLanguage(),_keys,_str);
		
		// エラーのハンドル(皆さん適当に)
		for(_i in _retArray){
			// 入力フィールドの色をピンクにする。
			Dom.setStyle(_id, 'background-color', 'pink');
			Dom.get(_resId).innerHTML = _retArray[_i];
		}
	};

	/**
	* ブラウザー言語の取得
	*   これはうまく動かない(不要??)
	*/
	var getLanguage = function(){

		// ブラウザロケールの取得
		if (window.navigator.language){
			// for FF, Chrome, Safari
			_locale = window.navigator.language.toLowerCase();
		} else if (window.navigator.userLanguage){
			// For IE
			_locale = window.navigator.userLanguage.toLowerCase();
		}

		return _locale;
	}
	
	return{

	/**
	* 初期処理
	*/
    	init: function() {

    		// 検証オブジェクトの初期化
			valObj 	= new MyValidator();

			// HTMLInputElementの配列を取得する。
			var _inObj = document.getElementsByTagName('input');
			for(var i = 0; i < _inObj.length; i++){
                                // 2009/4/20 追加
                if(_inObj[i].getAttribute('type') == 'text'){
				// focusとblurにイベントを仕込む
				Event.addFocusListener(_inObj[i].id, onFocusHdlr, _inObj[i]);		    
				Event.addBlurListener(_inObj[i].id, onBlurHdlr, _inObj[i]);
                                }
			}
			
		} // initの終わり
	
	};
}();

</script> 

</HEAD>

<BODY onload="validate.init()">
<h3>
MyValidatorの確認用
</h3>

<form method="post" action='#'>
<!--// 入力検証項目 -->

未入力のチェック;
<input id="v_required" class="ez_initguide isRequired" type="text" 
	value="入力してください.">
<div id="v_required_res"></div>

<br>
アルファベットのチェック;
<input id="v_alpha" class="ez_initguide isAlpha" type="text" size=50
	value="アルファベットで入力してください.">
<div id="v_alpha_res"></div>

<br>
アルファベット(大文字)のチェック;
<input id="v_capalpha" class="ez_initguide isCapAlpha" type="text" size=50 
	value="大文字のアルファベットで入力してください.">
<div id="v_capalpha_res"></div>

<br>
ひらがなのチェック;
<input id="v_hiragana" class="ez_initguide isHiragana" type="text" size=50
	value="ひらがなで入力してください.">
<div id="v_hiragana_res"></div>

<br>
カタカナのチェック;
<input id="v_katakana" class="ez_initguide isKatakana" type="text" size=50
	value="カタカナで入力してください.">
<div id="v_katakana_res"></div>

<br>
メイルアドレスのチェック;
<input id="v_email" class="ez_initguide isEmail" type="text" size=50
	value="abc.efg@hij.klm.op">
<div id="v_email_res"></div>

<br>
日付のチェック;
<input id="v_date" class="ez_initguide isDate" type="text" size=50
	value="yyyy/mm/dd">
<div id="v_date_res"></div>

</form>

<br>
</body>
</HTML>