#050
posted on 2021.08.02

(ネイティブJavaScript版) フォームの入力内容をチェックしてエラーメッセージ表示。

HTML5でフォーム内の要素に付加したrequired属性でのバリデーションが効かないことがあったので、JavaScriptでフォームの入力内容をチェックしてエラーメッセージを表示する方法のメモ。

 

HTML5によるバリデーションは、下記のどれかに該当すると機能しなくなる。

  • form要素にHTML5のnovalidate属性がある場合。
  • input要素にHTML5のformnovalidate属性がある場合。
  • submitボタンのイベントをJavaScriptのpreventDefault()メソッドで制御している場合。
  • HTML5によるバリデーションが発火する前にJavaScriptでform要素にsubmit()メソッドを実行している場合。

 

 

JavaScriptでバリデーションする手順

  1. submitボタンをクリックしたとき、または入力フィールドからフォーカスが外れたときに入力内容をチェックしてエラー判定を行う。
  2. エラーだった場合は動的に任意のクラス名を付加し、そのクラス名によってCSSでエラー表示をする。
  3. すべてのフィールドにエラー判定が無ければform要素でsubmit()メソッドを実行して送信。

 

 

HTMLのマークアップとCSSの設定

入力チェックが必要なフィールドの要素に任意のクラス名(「field-required」)を付け、その要素に入力されている値をJavaScriptで取得してエラーチェックする。

 

HTML

input要素とtextarea要素にはCSSの疑似要素が使えないのでdiv要素で囲い、エラーメッセージはdiv要素の疑似要素で表示させる。

エラー判定した場合、入力フィールドの親div要素(「.block-form-required」)にJavaScriptで動的にクラス名(「field-invalid」)を付加する。

<form id="form-vali" action="送信先URL" method="送信メソッド">
	<div class="block-form-required">
		<input class="field-required" type="text" name="name" placeholder="名前">
	</div>
	<div class="block-form-required">
		<input class="field-required" type="email" name="email" placeholder="メールアドレス">
	</div>
	<div class="block-form-required">
		<input class="field-required" type="tel" name="tel" placeholder="電話番号">
	</div>
	<div class="block-form-required">
		<textarea class="field-required" name="message" cols="50" rows="10"></textarea>
	</div>
	<button id="button-submit" type="submit">送信</button>
</form>

 

CSS

エラー用のクラス名(「field-invalid」)が動的に付加された場合に、疑似要素でエラーメッセージを表示させ、子要素(「.field-required」)のフィールドを赤枠にする。

.field-invalid::before {
	content: "このフィールドは必須です。";
	display: block;
	color: red;
}
.field-invalid input.field-required,
.field-invalid textarea.field-required {
	border: 1px solid red;
}

 

 

JavaScriptで入力内容を確認してエラー判定

入力内容をバリデーションする関数を作成し、submitボタンの「click」イベント、または入力フィールドの「blur」イベント(フォーカスが外れたら発火)で関数を実行する。

チェックの結果によってエラーメッセージ表示か送信実行の分岐処理をする。

(下記では、入力内容が空か空ではないかのみでエラーを判定。)

 

入力フィールドのイベントリスナーは実装の必要に応じて使い分ける。

  • 「blur」イベント : フォーカスが外れたときに発火。
  • 「focus」イベント : フォーカスしたときに発火。
  • 「change」イベント : フォーカスしたときとフォーカスが外れたときで入力内容に変化があれば発火。
  • 「input」イベント : 一文字入力されるごとに発火。

 

ネイティブJavaScript(Vanilla JavaScript)で実装

1. 必要な要素をDOMから取得、処理に使用する変数を宣言。

//DOMから指定要素を取得
const formEle = document.getElementById('form-vali'),//form要素
	submitButton = document.getElementById('button-submit'),//submitボタン
	formRequired = document.querySelectorAll('.block-form-required');//フィールドの親div要素

//使用するクラス名
const fieldRequired = 'field-required',//必須フィールドに付けたクラス名
	fieldInvalid = 'field-invalid';//エラー判定のとき動的に付加するクラス名

//エラー判定で使用する変数を宣言
let fieldValue,
	validFlg;

//フィールドの親div要素の数を取得
const formRequiredLength = formRequired.length;

document.getElementById(‘ID’) : 指定されたIDに一致する要素を表すElementオブジェクトを返す。無ければ「null」を返す。

document.querySelector(‘セレクター’) : 指定したCSSセレクターを取得する。最初に一致したHTMLElementオブジェクトだけを返す。無ければ「null」を返す。

Nodelistオブジェクト.length : Nodelist内のitemの数を返す。

 

2. バリデーションの関数を作成。

エラーの判定と、動的にクラス名を付与・削除する関数。

  1. 各入力フィールドのvalueの値を取得。
  2. 取得したvalueの値が空ならエラーと判定。
  3. エラーがあればフラグ用の変数「validFlg」に「false」を代入し、該当要素にエラー用のクラス名を付与。
  4. すべてのフィールドにエラーが無ければ変数「validFlg」は「true」を保持。
const ag2formVlidation = {
	//clickイベントですべてのフィールドのチェック用
	all: function(){
		//フラグをtrueにする
		validFlg = true;
		for(let i = 0; i < formRequiredLength; i++){
			//親div要素内からクラス名指定でvalue値を取得
			fieldValue = formRequired[i].getElementsByClassName(fieldRequired)[0].value;
			if(!fieldValue){
				//取得した値が空ならフラグをfalseにする
				validFlg = false;
				//親div要素にエラー表示用クラス名を付与
				if(!formRequired[i].classList.contains(fieldInvalid)) formRequired[i].classList.add(fieldInvalid);
			}else{
				//取得した値が空でなければ親div要素からエラー用クラス名を削除
				if(formRequired[i].classList.contains(fieldInvalid)) formRequired[i].classList.remove(fieldInvalid);
			}
		}
		return validFlg;
	},
	//blurイベントで個別にフィールドのチェック用
	each: function(event){
		//イベントが発火した要素を取得
		let thisField = event.target;
		fieldValue = thisField.value;
		if(!fieldValue){
			if(!thisField.parentNode.classList.contains(fieldInvalid)) thisField.parentNode.classList.add(fieldInvalid);
		}else{
			if(thisField.parentNode.classList.contains(fieldInvalid)) thisField.parentNode.classList.remove(fieldInvalid);
		}
	}
};

input要素.value : input要素を操作するためのHTMLInputElementインターフェイスのプロパティー。value属性の値を返す。任意の値を代入して設定することもできる。

要素.classList : 要素のclass属性を返す読み取り専用のプロパティー。DOMTokenListのコレクション。

要素.classList.contains(‘クラス名’) : 要素のclassList(DOMTokenList)に指定したクラス名が含まれていれば「true」、無ければ「false」を返す。

要素.classList.add(‘クラス名’) : 指定されたクラス名を要素のclassListに追加する。

要素.classList.remove(‘クラス名’) : 指定されたクラス名を要素のclassListから削除する。

event.target : イベントを発生させたオブジェクトへの参照。オブジェクトはその情報をプロパティーに持つ。

ノード.parentNode : 指定されたノードのDOMツリー内の親ノードを返す。要素の親ノードは、Elementノード、Documentノード、またはDocumentFragment。

 

3. submitボタンで「click」イベントが発生したときの処理。

submitボタンのイベントリスナーに、バリデーションとエラーが無ければ送信まで実行する関数を登録する。

//submitボタン用の関数を作成
function ag2submit(event){
	//通常の動作をキャンセル
	event.preventDefault();
	//バリデーションの関数(すべてのフィールドをチェック)を実行
	//返り値がtrueならform要素にsubmit()メソッドで送信処理
	if(ag2formVlidation.all()) formEle.submit();
}
//submitボタンのクリックイベントに上記の関数を登録
submitButton.addEventListener('click', ag2submit);

event.preventDefault() : eventオブジェクトのメソッド。発生したイベントの規定の動作を行わない。イベントの伝播は止めない。

document.フォーム要素.submit() : 指定したフォームを送信する。

対象要素.addEventListener(‘イベントのタイプ’, ‘関数’, ‘イベント伝播順’) : 対象要素に指定のイベントでコールする関数を指定して登録。第3引数(初期値 : false)でイベントの伝播する方向を指定できる。falseでDOM階層の下位から上位に伝播。

 

4. 入力フィールドで「blur」イベントが発生したときの処理。

クラス名が「field-required」のすべての要素(各入力フィールド)のイベントリスナーにバリデーションを実行する関数を登録する。

//バリデーションの関数(個別にフィールドをチェック)を登録
for(let i = 0; i < formRequiredLength; i++){
	formRequired[i].getElementsByClassName(fieldRequired)[0].addEventListener('blur', ag2formVlidation.each);
}

 

 

正規表現でバリデーション

入力フィールドが空かどうか以外のエラー判定をする場合は、取得したvalueの値を正規表現などでチェックする。

 

1. 入力が空白文字のみかをチェック

空白以外の文字が存在するかを正規表現で調べる。(存在しなければ空白文字のみなのでエラーにする。)

const ag2regExp = /[^\s]/;
if(!fieldValue.match(ag2regExp)){
	//エラー判定のときの処理
}

/正規表現パターン/フラグ : 正規表現オブジェクトnew RegExp()と同じ。JavaScriptでリテラルに宣言する場合には、「/」をデリミタに使う。

対象文字列.match(‘正規表現リテラルまたはRegExpオブジェクト’) : 対象文字列内にマッチする文字列があれば、その文字列を返す。無ければnullを返す。

[^] : 角括弧内で^の後ろに指定している文字以外にマッチ。

\s : [ \t\f\r\n]と同じ。垂直タブ以外のすべての空白文字にマッチ。

 

 

2. 入力が数字のみかをチェック

半角数字と全角数字以外の文字が存在するか正規表現で調べる。(数字以外があればエラーにする。)

const ag2regExp = /[^0-90-9]/;
if(fieldValue.match(ag2regExp)){
	//エラー判定のときの処理
}

/正規表現パターン/フラグ : 正規表現オブジェクトnew RegExp()と同じ。JavaScriptでリテラルに宣言する場合には、「/」をデリミタに使う。

対象文字列.match(‘正規表現リテラルまたはRegExpオブジェクト’) : 対象文字列内にマッチする文字列があれば、その文字列を返す。無ければnullを返す。

[^] : 角括弧内で^の後ろに指定している文字以外にマッチ。

: 角括弧内でのみ使用できる範囲を指定するメタ文字。

 

 

3. 入力文字数制限をしたときに文字数をチェック

JavaScriptでstring(文字列)が持つlengthプロパティーでは正確な文字数のチェックができない。

  • string(文字列)のlengthプロパティーは、UTF-16での文字コード(コードユニット)の数でカウントされる。
  • 通常、全角でも半角でもコードユニットは1つなので1文字ごとに1でカウントされる。
  • サロゲートペアという絵文字や異体文字を表す特殊な文字コードが存在し、UTF-16では2つのコードユニットの組み合わせで構成されるので、1文字が2とカウントされる。

※ サロゲートペアにも対応した厳格な文字数のチェックをするには対処が必要。

 

[ サロゲートペアに関する要約 ]

  • すべての文字に振られている固有IDを、UnicodeではCode Point、16ビット(2バイト)の符号単位で表現するUTF-16ではCode Unitと呼ぶ。
  • Unicodeの未使用領域に新たに加えられたサロゲートペアと呼ばれる文字は16ビットを超えているので、UTF-16では2つのCode Unitの組み合わせ(32ビット)で表現する。
  • サロゲートペアに使う領域には文字が割り振られていないので他の文字との重複はない。
  • UTF-16で利用されるサロゲートペアのCode Unitは、ハイサロゲート(前半2バイト)とローサロゲート(後半2バイト)からそれぞれひとつずつの組み合わせ(計4バイト)で構成される。
  • UTF-16で利用されるサロゲートペアのCode Unitの領域。
    ハイサロゲート : \uD800 ~ \uDBFF
    ローサロゲート : \uDC00 ~ \uDFFF
  • 文字列中にハイサロゲートとローサロゲートのCode Unitが並んだ場合に、1文字(UnicodeでのCode Point)として扱われる。

サロゲートペア(代用対)は16ビットUnicodeの領域1024文字分を2つ使い(前半 U+D800 〜 U+DBFF、後半 U+DC00 〜 U+DFFF)、各々1個ずつからなるペアで1024 × 1024 = 1,048,576文字を表す。これはちょうど16面分であり、第1面〜第16面(U+010000 〜 U+10FFFF)の文字をこれで表すこととした。加えて第0面(基本多言語面)も使用可能なので、Unicodeには合計で 1,048,576 + 65,536 – 2,048 = 111万2,064文字分の空間が確保されたことになる。Unicodeの符号空間が10FFFF16まで(サロゲート領域を除いて111万2064文字)とされているのはUTF-16が表現可能な限界だからである。(Wikipedia)

 

当初基本多言語面は以下のような4つの「領域」に分けられていた。

・0000 33FFをアルファベット及び音節文字の用字並びに種々の記号のために使うA領域
・3400 9FFFを中国、日本及び韓国の統合された漢字のために使うI領域
・A000 DFFFを将来の標準化のために使うO領域
・E000 FFFDを私用文字、互換文字と特殊文字の為に使うR領域

しかしながら上記のように例外的な配置が増えてきたため現在基本多言語面で「領域」として定められているのは以下の二つだけである。

・D800 DFFFを代用符号位置に使用するS領域
・E000 F8FFを私用領域

(Wikipedia)

 

U+D800 ~ U+DBFF と U+DC00 ~ U+DFFF の2,048個の符号位置は文字ではなく、サロゲートペア(代用対)に使う代用符号位置(Surrogate Code Point)となっている。上位側の表現に使う U+D800 ~ U+DBFF をHigh-Surrogate Code Point、下位側の表現に使う U+DC00 ~ U+DFFF をLow-Surrogate Code Pointと言う。(weblio辞書)

 

入力された文字数が10文字以下になっているかチェックするエラー判定。

サロゲートペアに対応したコードユニットの数を正規表現で調べる。(「null」か「10」より多ければエラーにする。)

  1. UFT-16の文字列の内容を正規表現で調べる。
    (JavaScriptの内部処理はUFT-16なので自動的にエンコードされている。)
  2. 正規表現でコードユニットのエスケープシーケンスを使って、サロゲートペアに該当する2組のコードユニットの文字列とそれ以外のコードユニットの文字列を順番に調べ、配列として取得する。
  3. 取得した配列の要素数がサロゲートペアとそれ以外のコードユニットの数なので、入力された本来の文字数が得られる。
  4. マッチするものがひとつも無ければ「null」が返る。

※ 文字数の判定以外にも、入力された文字からすべてのサロゲートペアを抽出できるように関数を作成。サロゲートペアの入力があればエラー判定にして表示する場合など必要に応じて利用。

//文字列内のサロゲートペアをチェックする関数
const ag2stringCheck = {
	//サロゲートペアに対応した文字数を保持
	length: function(str){
		//サロゲートペアの文字またはそれ以外の文字にマッチしたら順番に配列化
		let result = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g);
		//配列の要素数を取得
		if(result) result = result.length;
		//結果を返す
		return result;
	},
	//サロゲートペアの文字だけを配列にして保持
	Surrogates: function(str){
		//サロゲートペアの文字にマッチしたら順番に配列化
		return str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g);
	}
};

const valiNum = 10;//文字数
if(ag2stringCheck.length(fieldValue) === null || ag2stringCheck.length(fieldValue) > valiNum){
	//エラー判定のときの処理
}

対象文字列.match(‘正規表現リテラルまたはRegExpオブジェクト’) : 対象文字列内にマッチする文字列があれば、その文字列を返す。無ければnullを返す。

/正規表現パターン/フラグ : 正規表現オブジェクトnew RegExp()と同じ。JavaScriptでリテラルに宣言する場合には、「/」をデリミタに使う。

[] : 角括弧に含まれるいずれか1文字にマッチ。角括弧内で使用できるメタ文字(「-」「^」「\」「]」)はエスケープが必要。

: 角括弧内でのみ使用できる範囲を指定するメタ文字。

| : 左右辺の文字列のいずれかにマッチ。

[^] : 角括弧内で^の後ろに指定している文字以外にマッチ。

g : (global)フラグ。正規表現全体に一致したすべての結果を返し、キャプチャグループは返さない。(2番目、3番目…以降にマッチする部分も検索して配列として返す。) gフラグが無かった場合、最初に完全に一致したものと、そのキャプチャグループを返す。

文字列.lenght : stringインスタンスの読み取り専用データプロパティー。指定された文字列のUTF-16でのCode Unitの数を返す。

配列.lenght : Array型のインスタンスであるオブジェクトのプロパティー。配列の要素数を返す、または数値を代入して要素数を設定する。

 

※ 「スプレッド構文」を使えば正規表現を使わなくてもサロゲートペアに対応した正しい文字数を取得できるがIEが非対応。(ブラウザ対応状況はcan i use参照。)

const ag2stringCheck = {
	length: function(str){
		let result = [...str];
		if(result) result = result.length;
		return result;
	}
};

…反復可能オブジェクト : スプレッド構文。配列式や文字列などの反復可能オブジェクトを、記述した場所で展開する。

 

 

4. 入力されたメールアドレスの形式が正しいかをチェック

メールアドレスの基本構造(「ローカル部」+「@」+「ドメイン」)で構成されているかを正規表現で調べる。

※ 使用可能な文字や形式などは通信キャリアや送信システムで異なり、メールアドレスのフォーマットを規定している文書のRFC 5321やRFC 5322も改訂されることがあるようなので、あまり厳格にチェックしない方が無難。

 

入力されたメールアドレスの構造を正規表現でチェックしてエラー判定。

入力された文字列が、「半角英数字か記号が1文字以上」+「@」+「半角英数字か_.-が1文字以上」+「.」+「半角英数字か_-が1文字以上」の構造になっているか正規表現で調べる。(構造がマッチしなければエラーにする。)

const ag2regExp = /^[\w.\-!#$%&'*+/=?\^_`{|}~(),:;<>@[\]"\\]+@[\w.\-]+\.[\w\-]+$/;
if(!fieldValue.match(ag2regExp)){
	//エラー判定のときの処理
}

/正規表現パターン/フラグ : 正規表現オブジェクトnew RegExp()と同じ。JavaScriptでリテラルに宣言する場合には、「/」をデリミタに使う。

対象文字列.match(‘正規表現リテラルまたはRegExpオブジェクト’) : 対象文字列内にマッチする文字列があれば、その文字列を返す。無ければnullを返す。

^ : 直後に指定した文字が行の先頭にある場合にマッチ。

[] : 角括弧に含まれるいずれか1文字にマッチ。角括弧内で使用できるメタ文字(「-」「^」「\」「]」)はエスケープが必要。

\w : [a-zA-Z_0-9]と同じ。すべての半角英数字とアンダーバーにマッチ。

+ : 直前の指定した文字が1回以上繰り返す場合にマッチ。

$ : 直前に指定した文字が行の末尾にある場合にマッチ。

 

 

5. 入力されたURLの形式が正しいかをチェック

入力された文字列が、「httpsかhttp」+「://」+「任意の1文字以上」+「.」+「任意の1文字以上」の構造になっているか正規表現で調べる。(構造がマッチしなければエラーにする。)

※ 日本語URLなどもあるのですべての文字を入力可能として判定する。

const ag2regExp = /^https?:\/\/.+\..+$/;
if(!fieldValue.match(g2regExp)){
	//エラー判定のときの処理
}

/正規表現パターン/フラグ : 正規表現オブジェクトnew RegExp()と同じ。JavaScriptでリテラルに宣言する場合には、「/」をデリミタに使う。

^ : 直後に指定した文字が行の先頭にある場合にマッチ。

? : 直前の文字が0個か1個の場合にマッチ。

+ : 直前の指定した文字が1回以上繰り返す場合にマッチ。

$ : 直前に指定した文字が行の末尾にある場合にマッチ。

対象文字列.match(‘正規表現リテラルまたはRegExpオブジェクト’) : 対象文字列内にマッチする文字列があれば、その文字列を返す。無ければnullを返す。

 

 

この記事をシェア

この記事へのコメント

コメントの書き込みはまだありません。

  • コメント内のタグはエスケープ処理され、文字列として出力されます。
  • セキュリティーのため、投稿者のIPアドレスは取得されます。
  • 管理者が内容を不適切と判断したコメントは削除されます。
  • このフォームにはスパム対策として、Googleの提供するreCAPTCHAシステムが導入されています。
    (Google Privacy Policy and Terms of Service.)