サーバでは常にデータを検証するべきですが、追加のデータ検証を Web ページ自身で行うことにも多くの利点があります。いろいろな点で、ユーザはフォームに悩まされています。ユーザがフォームに入力している間にデータを検証することで、ユーザは何らかのミスをしたことを直ちに知ることができます。これはユーザが HTTP のレスポンスを待つ時間を減らし、またサーバで誤ったフォーム入力を扱うことがないようにします。本記事では、フォームの Web コンテンツでデータを検証する方法について扱います。
ブラウザが提供するフォーム検証を利用する
HTML5 の機能のひとつとして、スクリプトに頼ることなくほとんどのユーザデータを検証できることがあります。これはフォーム関連要素の検証関連属性で行われます。
要素が不正であるとき
要素が不正な状態であるとき、2 つのことが起こります:
- 要素が
:invalid
CSS 疑似クラスにマッチします。これにより、不正な要素に特定のスタイルを適用できます。同様に、正当な状態の要素は:valid
疑似クラスにマッチします。 - ユーザがデータを送信しようとすると、ブラウザはフォームをブロックしてエラーメッセージを表示します。
<input>
要素の検証制約
すべての <input>
要素は、pattern
属性を使用して検証することができます。この属性は値として、大文字・小文字を区別した正規表現を想定します。要素の値が空ではなく、また pattern
属性で指定された正規表現にマッチしないとき、その要素は不正であるとみなされます。
例
<form> <label for="choose">Would you prefer a banana or a cherry?</label> <input id="choose" name="i_like" pattern="banana|cherry"> <button>Submit</button> </form>
input:invalid { border: 1px solid red; } input:valid { border: 1px solid green; }
この例では、<input>
要素は使用可能な値 3 つのうちいずれかを受け入れます: 空文字列、文字列 "banana"、または文字列 "cherry" です。
required 属性
フォームを送信する前に、ある要素で値が必要である場合は、その要素を required
属性でマークするとよいでしょう。この属性が真であるとき、フィールドは空にすることができません。
<form> <label for="choose">Would you prefer a banana or cherry?</label> <input id="choose" name="i_like" pattern="banana|cherry" required> <button>Submit</button> </form>
input:invalid { border: 1px solid red; } input:valid { border: 1px solid green; }
前の例とフィールドのボーダーが異なることに注意してください。
注意: type
属性が email
または url
である <input>
要素では、検証のための pattern
属性は不要です。email
タイプを指定すると、フィールドの値が正しい形式の電子メールアドレス (multiple
属性がある場合は、カンマ区切りの電子メールアドレスのリスト) であることを要求します。url
タイプのフィールドは、自動的に適切な形式の URL を要求します。
その他の検証制約
ユーザの入力を受け入れるすべてのフォーム関連要素 (<textarea>
、<select>
など) は required
属性をサポートしますが、<textarea>
要素は pattern
属性をサポートしないことは知っておくべきです。
すべてのテキストフィールド (<input>
または <textarea>
) は maxlength
属性を使用してサイズを制限できます。フィールドの値が maxlength
属性で指定した値を超えると、そのフィールドは不正になります。ただしほとんどの場合、ブラウザはユーザがテキストフィールドで想定される値より長く入力することを許可しません。
数値フィールドでは、min
属性と max
属性も検証制約を提供します。フィールドの値が min
属性を下回ったり、max
属性を上回ったりすると、そのフィールドは不正になるでしょう。
こちらが全体的な例です:
<form> <p> <fieldset> <legend>Title<abbr title="This field is mandatory">*</abbr></legend> <input type="radio" required name="title" id="r1" value="Mr" ><label for="r1">Mr. </label> <input type="radio" required name="title" id="r2" value="Ms"><label for="r2">Ms.</label> </fieldset> </p> <p> <label for="n1">How old are you?</label> <!-- pattern 属性は数値フィールドでは必要ありませんが、 Firefox のように数値フィールドは実装していないが pattern 属性を サポートするブラウザ向けの代替策として動作できます。 --> <input type="number" min="12" max="120" step="1" id="n1" name="age" pattern="\d+"> </p> <p> <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory">*</abbr></label> <input type="text" id="t1" name="fruit" list="l1" required pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range"> <datalist id="l1"> <option>Banana</option> <option>Cherry</option> <option>Apple</option> <option>Strawberry</option> <option>Lemon</option> <option>Orange</option> </datalist> </p> <p> <label for="t2">What's your e-mail?</label> <input type="email" id="t2" name="email"> </p> <p> <label for="t3">Leave a short message</label> <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea> </p> <p> <button>Submit</button> </p> </form>
body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; margin: 0; padding: 0 5px; } p > label { display: block; } input[type=text], input[type=email], input[type=number], textarea, fieldset { /* WebKit ベースのブラウザでフォーム関連要素に きちんとスタイルを設定するために必要 */ -webkit-appearance: none; width : 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } input:invalid { box-shadow: 0 0 5px 1px red; } input:focus:invalid { outline: none; }
独自のエラーメッセージ
前出の例で見てきたように、ユーザが不正なフォームを送信しようとするたびにブラウザはエラーメッセージを表示します。このメッセージを表示する方法は、ブラウザにより異なります。
これらの自動メッセージには、欠点が 2 つあります:
- CSS でメッセージのルックアンドフィールを変更するための標準的な方法がありません。
- メッセージはブラウザのロケールに依存しており、ある言語のページでエラーメッセージが別の言語で表示されることがあります。
ブラウザ | レンダリング |
---|---|
Firefox 17 (Windows 7) | |
Chrome 22 (Windows 7) | |
Opera 12.10 (Mac OSX) |
これらのメッセージの外見やテキストを変更するには、JavaScript を使用しなければなりません。HTML や CSS だけで変更する方法はありません。
HTML5 では、フォーム要素の状態を確認したりカスタマイズしたりするための constraint validation API を提供します。特に、エラーメッセージのテキストを変更できます。簡単な例を見てみましょう:
<form> <label for="mail">I would like you to provide me an e-mail</label> <input type="email" id="mail" name="mail"> <button>Submit</button> </form>
JavaScript で、setCustomValidity()
メソッドを呼び出します:
var email = document.getElementById("mail"); email.addEventListener("keyup", function (event) { if(email.validity.typeMismatch) { email.setCustomValidity("I expect an e-mail, darling!"); } else { email.setCustomValidity(""); } });
JavaScript を使用してフォームを検証する
ネイティブのエラーメッセージを制御したい場合や、HTML5 のフォーム検証をサポートしないブラウザに対処したい場合は、JavaScript を使用するしかありません。
HTML5 constraint validation API
多くのブラウザが constraint validation API をサポートしてきており、頼れるようになってきました。この API は各フォーム要素で使用できるメソッドやプロパティで構成されています。
Constraint validation API のプロパティ
プロパティ | 説明 |
---|---|
validationMessage |
コントロールが満たしていない検証制約を (もしあれば) 表す、ローカライズされたメッセージ、またはコントロールが検証制約の候補ではない場合 (willValidate が false ) に空文字列、または要素の制約を満たす値です。 |
validity |
要素の検証状態を表す ValidityState オブジェクトです。 |
validity.customError |
要素が独自のエラー状態である場合に true を返します。そうでない場合は false を返します。 |
validity.patternMismatch |
要素の値が与えられたパターンにマッチしない場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスにマッチするでしょう。 |
validity.rangeOverflow |
要素の値が与えられた最大値を超える場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスおよび :out-of-range 疑似クラスにマッチするでしょう。 |
validity.rangeUnderflow |
要素の値が与えられた最小値を下回る場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスおよび :out-of-range 疑似クラスにマッチするでしょう。 |
validity.stepMismatch |
要素の値が step 属性で与えられた規則に合わない場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスおよび :out-of-range 疑似クラスにマッチするでしょう。 |
validity.tooLong |
要素の値が与えられた長さより長い場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスおよび :out-of-range 疑似クラスにマッチするでしょう。 |
validity.typeMismatch |
要素の値が正しい構文ではない場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスにマッチするでしょう。 |
validity.valid |
要素の値で妥当性の問題がない場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :valid 疑似クラスにマッチするでしょう。そうでない場合は CSS の :invalid 疑似クラスにマッチするでしょう。 |
validity.valueMissing |
要素が入力必須のフィールドであるのに値がない場合に true を返します。そうでない場合は false を返します。これが true を返す場合、要素は CSS の :invalid 疑似クラスにマッチするでしょう。 |
willValidate |
フォームが送信されるときに要素が検証される場合に true を返します。そうでない場合は false を返します。 |
Constraint validation API のメソッド
メソッド | 説明 |
---|---|
checkValidity() |
要素の値で妥当性の問題がない場合に true を返します。そうでない場合は false を返します。要素が不正である場合、このメソッドは要素で invalid イベントを発生させます。 |
setCustomValidity(message) |
要素に独自のエラーメッセージを追加します。独自のエラーメッセージを設定すると、要素が不正であるとみなされる場合に指定したエラーが表示されます。これにより JavaScript で、標準の constraint validation API で提供されるもの以外の検証失敗状態を作り出すことができます。ユーザに問題を報告する際に、メッセージが表示されます。 引数が空文字列である場合は、独自のエラーがクリアされます。 |
古いブラウザ向けに、constraint validation API の欠落を補うための H5F といったポリフィル を使用できます。いずれにせよすでに JavaScript を使用していますので、ポリフィルを使用することが Web サイトや Web アプリケーションの設計や実装での追加負担にはなりません。
constraint validation API の使用例
独自のエラーメッセージを作成するために API を使用する方法を見ていきましょう。始めに、HTML です:
<form novalidate> <p> <label for="mail"> <span>Please enter an email address:</span> <input type="email" id="mail" name="mail"> <span class="error" aria-live="polite"></span> </label> <p> <button>Submit</button> </form>
このサンプルフォームでは、ブラウザの自動検証を無効にするため novalidate
属性を使用しています。これで、検証を制御するためにスクリプトを使用できます。ただし、これは constraint validation API のサポートや CSS の疑似クラス :valid
、:invalid
、:in-range
および :out-of-range
の適用は無効にしません。つまり、データを送信する前にブラウザが自動的なフォームの妥当性確認を行わないとしても、あなた自身で確認を行って、フォームの状態に応じたスタイル設定ができます。
aria-live
属性は、スクリーンリーダーのような支援技術を使用している人々を含む皆に、独自のエラーメッセージを提示するようにします。
CSS
この CSS はフォームへのスタイル設定と、エラー出力をより目立つようにします。
/* これはサンプルの見栄えをよくするスタイルです */ body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; } p * { display: block; } input[type=email]{ -webkit-appearance: none; width: 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } /* これは不正なフィールド向けのスタイルです */ input:invalid{ border-color: #900; background-color: #FDD; } input:focus:invalid { outline: none; } /* これはエラーメッセージ向けのスタイルです */ .error { width : 100%; padding: 0; font-size: 80%; color: white; background-color: #900; border-radius: 0 0 5px 5px; -moz-box-sizing: border-box; box-sizing: border-box; } .error.active { padding: 0.3em; }
JavaScript
以下の JavaScript コードは独自のエラー検証を制御します。
// DOM ノードの選択法はたくさんあります。ここではフォーム自体、メールアドレス // 入力ボックス、そしてエラーメッセージを配置する span 要素を取得しています。 var form = document.getElementsByTagName('form')[0]; var email = document.getElementById('mail'); var error = document.querySelector('.error'); email.addEventListener("keyup", function (event) { // ユーザが何かを入力するたびに、メールアドレスのフィールドが妥当かを // 確認します。 if (email.validity.valid) { // エラーメッセージを表示している場合に、フィールドが妥当になれば // エラーメッセージを取り除きます。 error.innerHTML = ""; // メッセージの内容物をリセットします error.className = "error"; // メッセージの表示状態をリセットします } }, false); form.addEventListener("submit", function (event) { // ユーザがデータを送信しようとするたびに、メールアドレスの // フィールドが妥当かをチェックします。 if (!email.validity.valid) { // フィールドが妥当ではない場合、独自のエラーメッセージを // 表示します。 error.innerHTML = "I expect an e-mail, darling!"; error.className = "error active"; // また、イベントをキャンセルしてフォームの送信を止めます。 event.preventDefault(); } }, false);
こちらが実際の結果です:
constraint validation API はフォーム検証を制御するための強力なツールであり、HTML および CSS のみで検証を行うよりもはるかにユーザインターフェイスをコントロールできます。
組み込み API を使用しないフォーム検証
時々、古いブラウザやカスタムウィジェットにおいて、constraint validation API を使用できない (または使用したくない) ことがあるでしょう。このような場合でも、フォームを検証するために JavaScript を使用できます。フォーム検証は、実際のデータの検証よりもユーザインターフェイスの問題が多くなります。
フォームを検証するために、あなたはいくつかの問いを自問しなければなりません:
- どのような検証を実施するべきか?
- どのようにデータを検証するかを決めなければなりません: 文字列演算、型変換、正規表現など。これはあなた次第です。フォームのデータは常にテキストであり、スクリプトには常に文字列として渡されることを忘れないようにしてください。
- フォームが妥当でない場合に何をするべきか?
- これは明らかに UI の問題です。フォームがどのように動作するかを決めなければなりません: それでもフォームのデータを送信しますか? エラー状態のフィールドを強調しますか? エラーメッセージを表示しますか?
- ユーザが不正なデータを直すために何をしますか?
- ユーザの不満を軽減する目的で、ユーザが入力内容を直せるよう案内するために、可能な限り役に立つ情報を提供することがとても重要です。明瞭なエラーメッセージはもちろん、ユーザが何を求められているかをわかるように前もって提案するべきです。
フォーム検証 UI の要件について深く学びたいのでしたら、ぜひ読むべきである有用な記事があります:
- SmashingMagazine: Form-Field Validation: The Errors-Only Approach
- SmashingMagazine: Web Form Validation: Best Practices and Tutorials
- Six Revision: Best Practices for Hints and Validation in Web Forms
- A List Apart: Inline Validation in Web Forms
constraint validation API を使用しない例
これまでのことを説明するため、古いブラウザでも動作するように前出のサンプルを作り替えてみましょう:
<form> <p> <label for="mail"> <span>Please enter an email address:</span> <input type="text" class="mail" id="mail" name="mail"> <span class="error" aria-live="polite"></span> </label> <p> <!-- 一部の古いブラウザでは `button` 要素で、`submit` を明示的に指定した `type` 属性が必要です --> <button type="submit">Submit</button> </form>
ご覧いただけるように、HTML はほとんど同じであり、HTML5 の部分を取り除いただけです。ARIA は特に HTML5 との関係はない独立した仕様ですので、ここでは残されています。
CSS
同様に、CSS も大きく変更する必要はありません。:invalid
疑似クラスから実クラスへの変更と、Internet Explorer 6 で動作しない属性セレクタの使用を避けただけです。
/* これはサンプルの見栄えをよくするスタイルです */ body { font: 1em sans-serif; padding: 0; margin : 0; } form { max-width: 200px; } p * { display: block; } input.mail { -webkit-appearance: none; width: 100%; border: 1px solid #333; margin: 0; font-family: inherit; font-size: 90%; -moz-box-sizing: border-box; box-sizing: border-box; } /* これは不正なフィールド向けのスタイルです */ input.invalid{ border-color: #900; background-color: #FDD; } input:focus.invalid { outline: none; } /* これはエラーメッセージ向けのスタイルです */ .error { width : 100%; padding: 0; font-size: 80%; color: white; background-color: #900; border-radius: 0 0 5px 5px; -moz-box-sizing: border-box; box-sizing: border-box; } .error.active { padding: 0.3em; }
JavaScript
JavaScript コードでは大きな変更があり、多くの面倒な作業が必要です。
// 古いブラウザで DOM ノードを選択する方法は少ない var form = document.getElementsByTagName('form')[0]; var email = document.getElementById('mail'); // 以下は DOM 内で次の兄弟要素にたどり着くための技です。 // これは容易に無限ループになることがあるため、危険です。 // 新しいブラウザでは、element.nextElementSibling を使用しましょう。 var error = email; while ((error = error.nextSibling).nodeType != 1); // HTML5 仕様書より var emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; // 多くの古いブラウザは addEventListener メソッドをサポートしていません。 // 以下はこれを扱うためのシンプルな方法です。なお唯一の方法ではありません。 function addEvent(element, event, callback) { var previousEventCallBack = element["on"+event]; element["on"+event] = function (e) { var output = callback(e); // `false` を返すコールバックはコールバックチェーンを止めて、 // イベントコールバックの実行を中断します。 if (output === false) return false; if (typeof previousEventCallBack === 'function') { output = previousEventCallBack(e); if(output === false) return false; } } }; // ここから検証制約の再構築ができます。 // CSS の疑似クラスに頼ることはできないため、メールアドレスフィールドで // valid/invalid クラスを明示的に設定しなければなりません。 addEvent(window, "load", function () { // ここで、フィールドが空かを確認しています (フィールドは必須入力ではありません) // 空でなければ、内容部が適切な電子メールアドレスかを確認します。 var test = email.value.length === 0 || emailRegExp.test(email.value); email.className = test ? "valid" : "invalid"; }); // ユーザがフィールドに入力したときに、何をするかを定義します。 addEvent(email, "keyup", function () { var test = email.value.length === 0 || emailRegExp.test(email.value); if (test) { email.className = "valid"; error.innerHTML = ""; error.className = "error"; } else { email.className = "invalid"; } }); // ユーザがデータを送信しようとしたときに何をするかを定義します。 addEvent(form, "submit", function () { var test = email.value.length === 0 || emailRegExp.test(email.value); if (!test) { email.className = "invalid"; error.innerHTML = "I expect an e-mail, darling!"; error.className = "error active"; // 一部の古いブラウザは event.reventDefault() メソッドをサポートしていません。 return false; } else { email.className = "valid"; error.innerHTML = ""; error.className = "error"; } });
結果は以下のようになります:
ご覧いただけるように、あなた自身で検証システムを構築するのは大変なことではありません。クロスプラットフォームで、またあなたが作成するであろうあらゆるフォームにおいて使用するのに包括的で十分なものにすることが難しい点です。フォーム検証を行うために利用できる、多くのライブラリがあります。使用するのをためらうべきではありません。いくつか例を挙げます:
- 単独のライブラリ
- jQuery プラグイン:
リモート検証
場合によっては、リモートで検証を実行することが役に立つかもしれません。このタイプの検証は、アプリケーションのサーバ側で保存している付加的なデータとユーザが入力したデータを結びつける際に必要になります。これの用途のひとつが、登録フォームでユーザ名を尋ねるときです。重複を避けるためにユーザ名が利用可能かを確認するには、ユーザにデータの送信を求めてからエラー状態のフォームを返すのではなく、AJAX リクエストを実施する方がスマートであす。
このような検証を実施するには、いくつか用心すべきことがあります:
- API やいくつかのデータを広く公開することが必要です。機微な情報が無いようにしてください。
- ネットワークの遅延により、非同期で検証を行うことが必要です。検証が正常に行われない場合でもユーザが邪魔されないようにするために、UI でいくらか作業が必要になります。
おわりに
フォーム検証は複雑な JavaScript を必要としませんが、ユーザについて注意深く考えることが必要です。ユーザが提供するデータを正しくするのを支援することをいつも忘れないようにしてください。最後に、以下のことを必ず行ってください:
- 明瞭なエラーメッセージを表示してください。
- 入力形式については寛容になってください。
- どこでエラーが発生しているかを正確に示してください (特に大きなフォームで)。