uhyohyo.net

JavaScript初級者から中級者になろう

十二章第二回 フォーム

HTML5になって、フォームが大きく進化しました。そこで、それをJavaScriptで扱う方法を紹介します。どのように進化したかについては、ぜひ調べてみましょう(HTML5講座)。

妥当性チェック

HTML5のフォームには、妥当性validity)というものがあります。これは要するに、入力内容が正しいかどうかということです。

そこでまず、あるフォームの妥当性をチェックするということをやってみましょう。

もちろん、各コントロール(inputとか)にそれぞれ妥当性というものがありますが、フォームが妥当であるということは、そのフォームに属する全てのコントロールが妥当である(入力内容が正しい)ということを表します。

入力内容が全て正しければそのフォームは送信してもよいということになるので、フォームが妥当であるとは、つまりそのフォームは送信できるということを表します。

それでは、JavaScriptでの方法ですが、簡単です。form要素のノードがあったら、そのノードのcheckValidityというメソッド(引数なし)を呼び出すと、true(妥当である)かfalse(妥当でない)の真偽値を返します。

サンプルを用意しました。

また、form要素だけでなく、input要素・select要素・button要素・fieldset要素などなどの各コントロールにも、妥当性チェックをするためのメソッドがあります。実は、こちらのほうがいろいろあって機能が豊富です。

まず、form要素と同様にcheckValidityがあります。これは、フォーム全体ではなく、その要素が妥当であるかどうかを判定できます。

ここで、これを利用したサンプルを用意してみました。「郵便番号」を入力するとそれに合わせた(今回は適当ですが)住所を表示するというサンプルです。

サンプル

このサンプルでは、郵便番号のinput要素にoninputという属性があるのが分かります。onで始まるのはイベント属性で、今回の場合inputというイベントです。これは、ユーザーによってinput要素の入力内容が変化した場合に起こるイベントで、今回のように、フォームの入力を監視する際に役立ちます。

HTML4時代にはchangeというイベントもありましたが、これはinput要素からフォーカスが外れないと発生しないなど使いにくいものでしたので、このHTML5で新しく導入されたinputイベントを利用する機会のほうが多いでしょう。

それで、inputイベントが発生したときに何が起こるかというと、

inp(this)

です。つまり、inpという関数を、引数thisで呼び出しているのです。イベント属性中でのthisは、この要素自身、つまりこのinputになります。inpという関数は下のほうで作ってあります。

関数inp内ではいろいろやっていますが、結局何をやっているかというと、「郵便番号」が正しく入力されていれば「住所」のところに住所を自動で入力するというだけです。

「郵便番号が正しく入力されている」とは、7桁の数字が入力されているということです。それをJavaScriptでどう表現するかというと、

input.checkValidity()

だけです。つまり、このinputが妥当であるかどうか調べているだけです。

それでは「7桁の数字」の部分はどう表現しているかというと、実はinput要素のpattern属性に書いてあります。

pattern="\d{7}"

これは正規表現です。これは「数字7桁」を表していますね。HTML5では、このように、入力内容を正規表現を用いて指定できるのです。ただ、これは部分一致ではなく、全体で一致しないといけません。だから、「a1234567」のように余計な物があってはいけません。

pattern属性がある場合、この正規表現に入力内容がマッチする場合に、妥当であると判断されます。ですから、JavaScript側からはcheckValidity()で妥当性をチェックするだけでいいのです。

また、このinput要素を見ると、title属性があります。

title="郵便番号は7桁の数字を入力して下さい。"

このように、入力内容に制限があり複雑な場合、title属性を用いて説明するのがよいとされています。試しに正しく入力せずに「送信」を押して見ると、このメッセージが表示されると思います。

また、もっと詳細な情報を取得するために、validityというプロパティがあります。これはValidityStateという種類のオブジェクトです。

このオブジェクトはいくつかのプロパティを持ち、全て真偽値です。まずvalidというプロパティは、その要素が妥当であるかどうかを返します。つまり、input要素のcheckValidity()と同じです。

そして、これがfalseだった場合、すなわち妥当ではないに、他のプロパティが活躍します。他のプロパティは全て、妥当ではない原因を表しています。以下に列挙します。

valueMissing
required属性(入力が必須)があるのに入力されていない場合true。
typeMismatch
type="email",type="url"の場合に、正しい書式でない場合にtrue。
patternMismatch
pattern属性で指定された条件に合っていない場合true。
tooLong
maxlength属性で決められた長さより長い場合にtrue。
rangeUnderflow
数値入力コントロールで、min属性よりも低い場合true。
rangeOverflow
数値入力コントロールでmax属性よりも高い場合true。
stepMismatch
数値入力コントロールで、strep属性で指定した単位とあわない場合true。
customError
独自エラー(後述)がある場合true。

これを用いて、妥当でない場合の細かい原因をチェックできるのです。

そして、独自エラーというものが出てきました。これは、HTMLが持つ機能だけでは表せないような複雑な条件である場合に、JavaScript側から「妥当である」とか「妥当でない」ということを決めてやることができるのです。

そのために使うメソッドが、input要素(など)が持つメソッドsetCustomValidityです。これは、引数を1つ、エラーメッセージを渡してやります。この関数を使用すると、その要素は妥当ではなくなります。

一度setCustomValidityで妥当でなくしたものが再び妥当になった場合には、setCustomValidityの引数を空文字列("")にして呼び出すことで独自エラーを解除できます。

ということで、奮発してまたサンプルを用意しました。よくある感じで、パスワードを2回入力して一致しないとだめというやつです。

サンプル

ここで、新しくforminputイベントというイベントが出てきているのが分かります。

これは、あるフォームの中でinputイベントが起きると、そのフォームのコントロール全てでforminputイベントが起こるというもので、すなわちそのフォームの変更を感知できます。

forminputイベントが起きたとき、関数inpを、そのフォーム(this.form)を引数として呼び出しています。関数inp内では、上のパスワード本体が正しく入力されているかチェックして、下のパスワードが上と一致する場合は妥当、一致しない場合は妥当でないとしていることがわかります。

試しに、上と下に違う内容を入力して「送信」を押すと「パスワードが一致しません。」というメッセージがでて送信できません。

ちなみに、formchangeというのも存在します。

ただし、2014年7月現在、forminputに対応しているのはOpera12だけです。メジャーなブラウザは対応する気がないのでしょうか。forminputを使わずに同じ動作を実現するには、パスワード入力フォームの両方にinputイベントを設定するのがよいでしょう。

最後に、input要素(など)が持つwillValidateプロパティを紹介します。これは真偽値で、その要素が制約バリデーション候補である(妥当か妥当でないかの判断の対象となる)かどうかです。というのも、たとえばtype属性が"hidden"とか、あるいは"button"とかの場合はユーザーが入力するものではないですから、妥当であるとか妥当でないとかいう概念が無いのです。

数値入力とステップ

数値入力のinput(type="number", type="range")には、ステップというものがあります。例えば、

<input type="number" min="0" step="10">

というinput要素では、値として0,10,20,30,40,…を入力できます。

このようなinputに対して、stepUp,stepDownというメソッドが使用できます。引数は数値1つで、引数をnとすると、n段階だけ数値を上げ/下げるメソッドです。

例えば上のinputで数値が0のとき、stepUp(3)を実行すると30になります。30の状態からstepDown(2)を実行すると10になるでしょう。

日付入力

日付入力のinput要素において、サーバーへ送信されるのは文字列ですが、この文字列形式はそのままではJavaScriptで扱いにくいです。そこで、JavaScriptで扱いやすい形式で取得する方法があります。

valueAsDateは、日付をDateオブジェクトで取得できるプロパティです。Dateオブジェクト自体は昔からありました。

またvalueAsNumberは、日付を1970年1月1日の0時からのミリ秒数を取得できます。