クライアント側のフォーム検証
データをサーバーへ送信する前に、必須のフォームコントロールが記入され、すべてのフォームコントロールが正しい書式で記入されていることを保証することが重要です。このクライアント側フォーム検証 (client-side form validation) は、送信されるデータが様々なフォームコントロールで指定されている要件を満たしていることを保証します。
この記事では、クライアント側フォーム検証の基本概念と例を紹介します。
| 前提知識: | コンピューターリテラシー、適度な HTML、CSS、JavaScript の理解。 |
|---|---|
| 目的: | フォーム検証とは何か、なぜ重要なのか、どのように実装するのかを理解すること。 |
クライアント側の検証は最初のチェックであり、ユーザーの使い勝手を良くするために重要な機能です。クライアント側で無効なデータを捕捉することで、ユーザーはすぐに修正できます。 もしも無効なデータがサーバーに送られてから拒否された場合、サーバーへの往復とクライアント側に戻ってユーザーにデータを修正するように指示することになり、かなり時間を浪費します。
しかし、クライアント側の検証はセキュリティ対策とは考えられません。アプリは常にサーバー側でもクライアント側と同様に送信されたデータのセキュリティをチェックしてください。なぜならクライアント側の検証は容易に回避することができて、悪意のあるユーザーは簡単に、サーバーへ無効なデータを送信できます。
メモ: 何が起こり得るかは ウェブサイトセキュリティを見てください。サーバー側の検証はこのガイドの範囲を超えますが、覚えておいてください。
フォーム検証とは何か
有名なサイトの登録フォームに行き、データを求められている書式で入力しないと、フィードバックがあることに気づくでしょう。 次のようなメッセージが表示されます。
- 「このフィールドは必須です」(このフィールドが空欄にできない場合)
- 「電話番号は XXX-XXXX の形式で入力してください」(あるデータ形式が必須の場合)
- 「有効なメールアドレスを入力してください」(入力データが適切な形式ではない場合)
- 「パスワードは 8 文字から 30 文字の間で、1 文字以上の大文字、記号、数字を含む必要があります。」(特に詳しいデータ形式が必要な場合)
これはフォーム検証と呼ばれます。 データを入力したとき、ブラウザー、またはウェブアプリケーションは、そのデータが正しい書式であり、アプリケーションに設定された制約に合っているかをチェックします。ブラウザーで行われる検証はクライアント側の検証と、サーバー側で行われるものはサーバー側の検証と呼ばれます。 この章では、クライアント側の検証に焦点を当てます。
情報が正しく書式化されていれば、アプリケーションはデータをサーバーに送信し、(通常は)データベースに保存することができます。情報が正しく書式化されていない場合は、修正すべき点を説明するエラーメッセージを表示して、ユーザーに再試行を促します。
私たちはできるだけ簡単にフォームを埋めてもらいたいわけですが、なぜフォームを検証する必要があるのでしょうか?理由は主に 3 つあります。
-
正しいデータを正しい書式で入力してほしい。 ユーザーのデータが誤った形式で格納されたり、ユーザーが正しい情報を入力しなかったり、省略したりすると、アプリケーションが正しく動作しないからです。
-
ユーザーのデータを保護したい。 ユーザーに安全なパスワードを入力させることで、アカウント情報を保護しやすくなります。
-
自分たちを守りたい。 悪意のあるユーザーが保護のないフォームを悪用して、そのフォームを一部に持つアプリケーションに危害を加える方法がたくさんあります。ウェブサイトセキュリティを参照してください。
警告: クライアントからサーバーに渡されたデータを信用しないでください。フォームが正しく検証を行い、クライアント側で悪意のある入力を防いでいるとしても、悪意のあるユーザーはネットワークリクエストを改ざんすることができます。
様々な種類のフォーム検証
ウェブで見かけるクライアント側のフォーム検証には 2 つの種類があります。
- 組み込みフォーム検証 HTML のフォーム属性で、どのフォームコントロールが必須であるか、また、ユーザー入力データが有効であるためにはどのような形式で入力されなければならないかを定義することができます。
- JavaScript フォーム検証 JavaScript は一般的に、HTML フォーム検証の強化やカスタマイズのために利用します。
クライアント側検証は、JavaScript をほとんど、あるいはまったく使用せずに実行することができます。HTML 検証は JavaScript よりも高速ですが、 JavaScript 検証よりもカスタマイズ性が低くなります。一般的に、フォームは堅牢な HTML 機能を使用して作成し、必要に応じて JavaScript で使い勝手を向上させることが推奨されます。
組み込みフォーム検証の利用
最新のフォームコントロールの機能の一つに、 JavaScript に頼らない多くのユーザーデータの検証があります。 これはフォーム要素の検証の属性を使って行います。 これまで多くを見てきましたが、まとめ直すと次のようになります。
required: フォームを送信する前に、フォームフィールドに入力する必要があるかどうかを指定します。minlengthとmaxlength: テキストデータ(文字列)の最小・最大長を指定します。min、max、step: 数値入力型の最小値と最大値、および最小値から始まる値の増分(段階)を指定します。type: データを数字にするか、メールアドレスにするか、その他のプリセットされた特定の型にするかを指定します。pattern: データが指定された正規表現に一致するかどうかを指定します。
入力データをこの指定されたルールに基いて検証します。検証にパスすれば有効で検証にパスしなければ有効ではないと考えます。
要素が有効な場合は、次のようになります。
- その要素は、 CSS の擬似クラス
:validに一致します。これにより、有効な要素に特定のスタイルを適用することができます。また、ユーザーがコントロールを操作した場合は、:user-validにも一致します。さらに、入力タイプや属性によっては、:in-rangeなどの他の UI 擬似クラスにも一致する可能性があります。 - ユーザーがデータを送信しようとした場合、送信を妨げるもの(JavaScript など)が何も指定されていない限り、ブラウザーはフォームを送信します。
要素の値が無効なときは、次のようになります。
- この要素は、CSS 擬似クラス
:invalidに一致します。 ユーザーがコントロールを操作した場合は、 CSS 擬似クラス:user-invalidにも一致します。 その他の UI 擬似クラス、例えば:out-of-rangeなどにも、エラー内容によっては一致する場合があります。 これらは、無効な要素に特定のスタイルを適用するために使用します。 - ユーザーがデータを送信しようとすると、ブラウザーがフォームの送信をブロックし、エラーメッセージを表示します。エラーメッセージはエラーの種類によって異なります。制約検証 API については下記で説明します。
入力要素の制約の検証
この節では、これまで述べてきたいくつかの属性をテストします。
基本スターターファイル
基本的な例から始めましょう。― 好きな果物を banana (バナナ)か cherry (サクランボ)から選べる入力欄があるとします。
テキストの <input> とそれに関連付けられた <label>、送信の <button> から成ります。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>好きな果物の出発点</title>
<style>
input:invalid {
border: 2px dashed red;
}
input:valid {
border: 2px solid black;
}
</style>
</head>
<body>
<form>
<label for="choose">banana と cherry のどちらが好き?</label>
<input id="choose" name="i_like" />
<button>送信</button>
</form>
</body>
</html>
始めるにあたり、前回の HTML コードを新しい index.html ファイルにコピーしてください。それをハードドライブ上の新しいディレクトリーに保存してください。
required 属性
よく使われる HTML の検証機能が required 属性です。
入力欄を必須にしたい場合は、この属性を要素に追加してください。
この属性が設定されていると、この要素が :required UI 擬似クラスに一致するようになり、入力欄が空であった場合にフォームが送信されずにエラーメッセージが表示されるようになります。
空のままでは、この入力は無効とみなされ、:invalid UI 擬似クラスに一致します。
同じ名前のグループ内のラジオボタンに required 属性が指定されている場合、グループを有効にするには、そのグループ内のラジオボタンのいずれかがチェックされている必要があります。チェックされたラジオは、属性を設定したものでなくてもかまいません。
メモ: ユーザーには必要なデータのみを入力させましょう。例えば、誰かの性別や役職を実際に知る必要があるでしょうか?
以下のように、 required 属性を入力欄に追加しましょう。
<form>
<label for="choose">banana と cherry のどちらが好き? *</label>
<input id="choose" name="i-like" required />
<button>送信</button>
</form>
メモ: 一般的な手法として、必須のフォームコントロールのラベルの後にアスタリスク(またはその他の印)を付けることで、視覚に障碍のないユーザーにも目立つようにしています。フォーム要素が必須であることをユーザーに示すことは、良い使い勝手を得るためだけでなく、WCAG アクセシビリティガイドラインで求められていることでもあります。
要素が必須、有効、無効な場合に適用される CSS スタイルを加えます。
input:invalid {
border: 2px dashed red;
}
input:invalid:required {
background-image: linear-gradient(to right, pink, lightgreen);
}
input:valid {
border: 2px solid black;
}
この CSS によって、入力が有効でない場合には、入力を赤の破線で境界線を描きますが、入力が有効な場合には、黒の直線で境界線を描きます。 要求された値があり、値が有効でないときは背景にグラデーションを追加します。つぎの例の動作を確認しましょう。
値を入力せずにフォームを送信してみてください。無効な入力欄がフォーカスされ、デフォルトのエラーメッセージ(「このフィールドは入力必須です。」)が表示されます。また、フォームの送信も抑止されます(ただし、値が入力された場合でも、MDN による埋め込みフォームの処理方法に起因するエラーを避けることができるため、フォームの送信を抑止している点にご注意ください)。
正規表現での検証
もう一つとてもよく使われる機能は pattern 属性で、値として正規表現を取ります。
正規表現 (regexp) はテキスト文字列の中の文字の組み合わせに一致させるために使うことができるため、フォームの検証には理想的であり、JavaScript と同様に様々な利用ができます。
正規表現はかなり複雑です。このモジュールでは正規表現のすべてを説明する意図はありません。 いくつかの例を挙げますのでどのように動作するか基本的な考えを把握してください。
a—aの 1 文字に一致する (bやaaなどではない)。abc—aと、その次のbと、その次のcの並びに一致する。ab?c—aと、その次にひとつだけbがあるかないかと、その次のcの並びに一致する (acまたはabc)ab*c—aと、その次に任意の数のbが続き、その次にcのある並びに一致する。(ac,abc,abbbbbc, 等)a|b— 一文字のaまたはbに一致するabc|xyz—abcの並びまたはxyzの並びに一致する。これはabcxyzやaやyなどには一致しない。
正規表現には多くの組合せがあるので例はここまでとします。完全な一覧や多くの例は、正規表現のドキュメントを参照してください。
使用例を実装しましょう。 HTML を更新して pattern 属性を追加しましょう。
<form>
<label for="choose">banana と cherry のどちらが好き?</label>
<input id="choose" name="i-like" required pattern="[Bb]anana|[Cc]herry" />
<button>送信</button>
</form>
これは下記の更新があります。次のものを使ってみてください。
Play ボタンをクリックして、MDN Playground でこの例を開くと、そこでソースコードを編集することも可能です。
この例では、<input> 要素は "banana"、"Banana"、"cherry"、"Cherry" という 4 つの文字列値のうち 1 つを受け付けます。正規表現は大文字小文字を区別しますが、中括弧にはさまれた "Aa" のパターンを使って小文字と同様に先頭が大文字のバージョンをサポートします。
この時点で、pattern 属性の中の値を以前に見たいくつかの例と同じ値に変更してみて、入力欄が有効になるように入力する値がどのように影響するかを確認してください。
自分で考えた値も書いてみて、どのようになるか確認しましょう。
果物に関する値を可能にすれば、例が分かりやすくなります。
もし <input> の空ではない値が正規表現パターンに一致しなかった場合、この input は :invalid 擬似クラスに一致します。空欄で、その要素が必須でない場合、それは無効なものと見なされません。
<input> 要素の型によっては、検証のために pattern 属性が必要ないことがあります。例えば email 型を指定すると、入力された文字列を、有効な形式のメールアドレスまたは、 multiple 属性がある場合はカンマで区切られたメールアドレスのリストであることを確認する正規表現で検証します。
メモ:
<textarea> 要素は pattern 属性に対応していません。
入力欄の長さの制約
<input> または <textarea> によって作成してすべてのテキストフィールドで文字数を制限したいときには minlength 属性と maxlength 属性が使用できます。
フィールドが値をもっており、その文字数が minlength の値より少ないか、文字数が maxlength の値より大きい場合は、フィールドは無効です。
ブラウザーはよくテキストフィールドに期待している以上に入力させないことがあります。単に maxlength を使うよりも良いユーザーエクスペリエンスは、入力文字数のフィードバックを提供してサイズ以下でコンテンツを編集できるようにもしておくことです。
この例のひとつが、ソーシャルメディアに投稿する際の文字数制限です。これは JavaScript と maxlength を使った解決策の組み合わせ実現できます。
メモ: 長さの制約は、値をプログラムで設定した場合は報告されません。ユーザーが指定した入力に対してのみ報告されます。
入力欄の値の制約
数値のフィールド (例えば <input type="number">) の場合、min 属性と max 属性によって入力に制限を加えられます。
もしそのフィールドの値がこの範囲を超える場合、そのフィールドは有効ではありません。
他の例を見てみましょう。
基本スターターファイルの新しいコピーを作成し、index2.html と同じディレクトリーに保存してください。
では、<body> 要素の中身を削除して、以下のように置き換えてください。
<form>
<div>
<label for="choose">banana と cherry のどちらが好き? *</label>
<input
type="text"
id="choose"
name="i-like"
required
minlength="6"
maxlength="6" />
</div>
<div>
<label for="number">どのくらい要りますか?</label>
<input type="number" id="number" name="amount" value="1" min="1" max="10" />
</div>
<div>
<button>送信</button>
</div>
</form>
- ここで、
textフィールドにはminlength属性とmaxlength属性には 6 を指定しています。これは banana (バナナ) や cherry (さくらんぼ) の文字数と同じです。 - またここでは、
numberフィールドにmin属性で 1 をmax属性で 10 を指定しました。 この範囲外の数値を入力すると、無効な数値として表示されます。また、増減する矢印を使用して、この範囲外に数値を移すことはできません。 ユーザーがこの範囲外の数値を手動で入力した場合、そのデータは無効なものと見なされます。 この数字は必須ではないので、値を除去すると有効になります。
例をライブで実行してみましょう。
Play ボタンをクリックして、MDN Playground でこの例を開くと、そこでソースコードを編集することも可能です。
数値の入力型、例えば range や date などは step 属性を取ることもでき、入力コントロール(数値の増加・減少ボタンなど)を使用するときに上げ下げすることができる値の増分を設定することができます。上の例では step 属性を入れていませんので、デフォルト値の 1 となります。つまり 3.2 のような浮動小数点数でも無効になります。
サンプル全体
HTML の内蔵検証機能の使い方を示す例の全体を示します。 まずは HTML から見てみましょう。
<form>
<p>必須 (*) のフィールドはすべて埋めてください。</p>
<fieldset>
<legend>運転免許を持っていますか? *</legend>
<input type="radio" required name="driver" id="r1" value="yes" />
<label for="r1">はい</label>
<input type="radio" required name="driver" id="r2" value="no" />
<label for="r2">いいえ</label>
</fieldset>
<p>
<label for="n1">何歳ですか?</label>
<input type="number" min="12" max="120" step="1" id="n1" name="age" />
</p>
<p>
<label for="t1">好きな果物は何ですか? *</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>バナナ</option>
<option>さくらんぼ</option>
<option>りんご</option>
<option>いちご</option>
<option>レモン</option>
<option>オレンジ</option>
</datalist>
</p>
<p>
<label for="t2">メールアドレスは何ですか?</label>
<input type="email" id="t2" name="email" />
</p>
<p>
<label for="t3">短いメッセージをどうぞ。</label>
<textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
</p>
<p>
<button>送信</button>
</p>
</form>
この HTML をスタイル設定する CSS は次の通りです。
form {
font: 1em sans-serif;
max-width: 320px;
}
p > label {
display: block;
}
input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
width: 100%;
border: 1px solid #333333;
box-sizing: border-box;
}
input:invalid {
box-shadow: 0 0 5px 1px red;
}
input:focus:invalid {
box-shadow: none;
}
これで次のようにレンダリングされます。
Play ボタンをクリックして、MDN Playground でこの例を開くと、そこでソースコードを編集することも可能です。
入力値と、それをサポートする入力型の制約に使える属性の完全なリストは、検証関連属性を見てください。
JavaScript を使用したフォーム検証
内蔵のエラーメッセージの見かけを制御したい場合は、JavaScript を使用する必要があります。 この節では、このようにするさまざまな方法を見ていきます。
制約検証 API
多くのブラウザーが制約検証 API に対応しています。以下のフォーム要素 DOM インターフェイスで利用できるメソッドとプロパティのセットで構成されています。
HTMLButtonElement(<button>要素を表現)HTMLFieldSetElement(<fieldset>要素を表現)HTMLInputElement(<input>要素を表現)HTMLOutputElement(<output>要素を表現)HTMLSelectElement(<select>要素を表現)HTMLTextAreaElement(<textarea>要素を表現)
制約検証 API には、上記の要素で利用できる、次のプロパティがあります。
-
validationMessage: コントロールが満たさない検証制約を記述したローカライズされたメッセージを返します(存在する場合)。コントロールが制約検証の候補でない場合(willValidateがfalse)、または要素の値が制約を満たしている場合(有効である場合)には、空の文字列を返します。 -
validity: 要素の検証状態を説明するValidityStateオブジェクトです。取りうる検証状態の詳細はValidityStateのリファレンスを参照してください。下記はよく使われるものを少し、一覧にしています。patternMismatch: 値が指定したpatternに一致しない場合trueを、一致する場合falseを返す。true なら、要素は:invalidCSS 擬似クラスに一致する。tooLong:maxlength属性で指定した最大値より値が長い場合trueを、同じ長さ以下の場合falseを返す。true なら、要素は:invalidCSS 擬似クラスに一致する。tooShort:minlength属性で指定した最小値より値が短い場合trueを、同じ長さ以上の場合falseを返す。true なら、要素は:invalidCSS 擬似クラスに一致する。rangeOverflow:max属性で指定し最大値より値が大きい場合trueを、同じ大きさ以下の場合falseを返す。true なら、要素は:invalidと:out-of-rangeCSS 擬似クラスに一致する。rangeUnderflow:min属性で指定し最小値より値が小さい場合trueを、同じ大きさ以上の場合falseを返す。true なら、要素は:invalidと:out-of-rangeCSS 擬似クラスに一致する。typeMismatch: 値が要求する構文でない場合(typeがemailかurlのとき)はtrueを、構文が正しい場合はfalseを返す。trueなら、要素は:invalidCSS 擬似クラスに一致する。valid: 要素が検証制約をすべて満たす、ゆえに有効とみなされる場合trueを、いずれかの制約を満たさない場合falseを返す。true なら、要素は:validCSS 擬似クラスに一致する。そうでない場合は:invalidCSS 擬似クラスに一致する。valueMissing: 要素にrequired属性があって値がない場合はtrueを、そうでない場合falseを返す。true なら、要素は:invalidCSS 擬似クラスに一致する。
-
willValidate: フォームが送信されるときに要素が検証される場合にtrueを返します。そうでない場合はfalseを返します。
また、制約検証 API では、上記の要素や form 要素に対して、以下のメソッドを利用することが可能です。
checkValidity(): 要素の値で有効性の問題がない場合にtrueを返します。そうでない場合はfalseを返します。要素が無効である場合、このメソッドは要素でinvalidイベントを発生させます。reportValidity(): イベントを使用して、無効なフィールドを報告します。このメソッドはonSubmitイベントハンドラーではpreventDefault()と組み合わせて使用すると有益です。setCustomValidity(message): 要素に独自のエラーメッセージを追加します。独自のエラーメッセージを設定すると、要素が無効であるとみなされる場合に指定したエラーが表示されます。これにより JavaScript で、標準の HTML 制約検証 API で提供されるもの以外の検証不合格状態を作り出すことができます。ユーザーに問題を報告する際に、メッセージが表示されます。
独自のエラーメッセージの実装
上記の HTML の検証制約の例で見てきたように、ユーザーが無効なフォームを送信しようとするたびにブラウザーはエラーメッセージを表示します。このメッセージを表示する方法は、ブラウザーにより異なります。
これらの自動のメッセージには、2 つの欠点があります。
- CSS でメッセージの表示方法を変更するための標準的な方法がありません。
- メッセージはブラウザーのロケールに依存しており、ある言語のページでエラーメッセージが別の言語で表示されることがあります。これは下記の Firefox スクリーンショットで見ることができます。

これらのメッセージの外見やテキストを変更するには、制約検証 API の最も一般的なユースケースです。 この使用法を例で詳しく見てみましょう。
まずは HTML から始めましょう。同様に、これを基本スターターファイルの別のコピーに貼り付けてみてください。
<form>
<label for="mail">
メールアドレスを教えてください:
</label>
<input type="email" id="mail" name="mail" />
<button>送信</button>
</form>
このページに次の JavaScript を追加します。
const email = document.getElementById("mail");
email.addEventListener("input", (event) => {
if (email.validity.typeMismatch) {
email.setCustomValidity("メールアドレスを入力してください。");
} else {
email.setCustomValidity("");
}
});
ここでメールアドレス入力への参照を保管して、入力値が変更されるたびに制約コードが走るためのイベントリスナーを追加します。
このコードの中で、 email 入力欄の validity.typeMismatch プロパティが true を返しているかどうか、つまり、含まれる値が正規の email アドレスのパターンに一致しないことをチェックしています。その場合、setCustomValidity()メソッドをカスタムメッセージとともに呼び出します。これにより、入力が無効となるため、フォームを送信しようとすると、送信に失敗し、カスタムエラーメッセージが表示されます。
validity.typeMismatch が falseの場合、空文字で setCustomValidity() メソッドを呼び出します。これは入力が有効となり、フォームが送信されます。検証中に、いずれかのフォームコントロールに空文字列ではない customError がある場合、フォームの送信はブロックされます。
下記で試すことができます(Play ボタンを押すと、MDN Playground でサンプルを実行し、ソースコードを編集できます)。
組み込みフォーム検証の拡張
前の例では、特定のエラー (validity.typeMismatch) に対してカスタマイズしたメッセージを表示させる方法を示しました。
また、組み込みのフォーム検証をすべて使用し、さらに setCustomValidity() を使用して追加することも可能です。
ここでは、組み込みの <input type="email"> の検証を拡張して、 @example.com ドメインのアドレスのみを受け入れるようにする方法を説明します。
下記 HTML の <form> から始めます。
<form>
<label for="mail">メールアドレス (@example.com のみ):</label>
<input type="email" id="mail" />
<button>送信</button>
</form>
検証コードを以下に記します。
新しい入力が有った場合、コードの最初の段階では、 setCustomValidity("") を呼んで独自の検証メッセージをリセットします。
次に、 email.validity.valid を使用して、入力されたアドレスが無効かどうかをチェックし、無効な場合はイベントハンドラーから戻ります。
これにより、入力されたテキストが有効なメールアドレスではない場合に、すべての通常の組み込み検証チェックが実行されることが保証されます。
メールアドレスが有効である場合、コードは独自の制約を追加し、アドレスが @example.com で終わっていない場合、エラーメッセージとともに setCustomValidity() を呼び出します。
const email = document.getElementById("mail");
email.addEventListener("input", (event) => {
// 組み込みの制約を検証
email.setCustomValidity("");
if (!email.validity.valid) {
return;
}
// 独自の検証を拡張
if (!email.value.endsWith("@example.com")) {
email.setCustomValidity("@example.com のメールアドレスを入力してください");
}
});
無効なメールアドレス、 @example.com で終わらない有効なメールアドレス、 @example.com で終わる有効なメールアドレスを送信してみてください。
より詳細な例
これまでほんとうに簡単な例を見てきましたので、少し複雑な独自の検証を作成するために API を使用する方法を見ていきましょう。
始めに、HTML です。一緒に構築してみましょう。
<form novalidate>
<p>
<label for="mail">
<span>メールアドレスを入力してください *:</span>
<input type="email" id="mail" name="mail" required minlength="8" />
<span class="error" aria-live="polite"></span>
</label>
</p>
<button>送信</button>
</form>
このフォームでは、 novalidate 属性を使用してブラウザーの自動検証を無効にしています。 フォームに novalidate 属性を設定すると、フォームが独自のエラーメッセージバブルを表示しなくなります。 その代わりに、独自の方法で DOM にカスタムエラーメッセージを表示することができます。
ただし、制約検証 API や、 :valid などの CSS 擬似クラスのアプリケーションの対応が無効になるわけではありません。
つまり、たとえブラウザーがデータを送信する前にフォームの有効性を自動的に調べないとしても、自分自身で調べ、それに応じてフォームのスタイル設定を行うことができるということです。
検証する入力は <input type="email">で、これは required (入力必須) で、8 文字の minlength があります。これをわれわれのコードで確認して、それぞれカスタムエラーメッセージを表示させてみましょう。
<span> 要素の中にエラーメッセージを表示させようとしています。 <span>にセットされた aria-live 属性は、スクリーンリーダーのような支援技術を使用している人々を含む皆に、独自のエラーメッセージを提示するようにします。
この CSS はフォームの見栄えを少し良くして、入力データが無効なときの見た目のフィードバックを提供します。
body {
font: 1em sans-serif;
width: 200px;
padding: 0;
margin: 0 auto;
}
p * {
display: block;
}
input[type="email"] {
appearance: none;
width: 100%;
border: 1px solid #333333;
margin: 0;
font-family: inherit;
font-size: 90%;
box-sizing: border-box;
}
/* 無効なフィールド */
input:invalid {
border-color: #990000;
background-color: #ffdddd;
}
input:focus:invalid {
outline: none;
}
/* エラーメッセージのスタイル */
.error {
width: 100%;
padding: 0;
font-size: 80%;
color: white;
background-color: #990000;
border-radius: 0 0 5px 5px;
box-sizing: border-box;
}
.error.active {
padding: 0.3em;
}
それでは、独自のエラー検証の実装 JavaScript を見てみましょう。 DOM ノードを選択する方法は数多くありますが、ここではフォーム自体とメールアドレスの入力ボックス、そしてエラーメッセージを表示する span 要素を取得します。
イベントハンドラーを使用して、ユーザーが何かを入力するたびに、フォームフィールドが有効であるかどうかを確認します。エラーがある場合は、それを表示します。エラーがない場合は、エラーメッセージをすべて削除します。
const form = document.querySelector("form");
const email = document.getElementById("mail");
const emailError = document.querySelector("#mail + span.error");
email.addEventListener("input", (event) => {
if (email.validity.valid) {
emailError.textContent = ""; // メッセージの内容物をリセットします
emailError.className = "error"; // メッセージの表示状態をリセットします
} else {
// それでもエラーが発生する場合は、正しいエラーを表示します。
showError();
}
});
form.addEventListener("submit", (event) => {
// メールアドレスのフィールドが無効な場合
if (!email.validity.valid) {
// 適切なエラーメッセージを表示
showError();
// フォーム送信を抑止
event.preventDefault();
}
});
function showError() {
if (email.validity.valueMissing) {
// 空であれば
emailError.textContent = "メールアドレスを入力する必要があります。";
} else if (email.validity.typeMismatch) {
// メールアドレスでなければ
emailError.textContent = "入力された値はメールアドレスでなければなりません。";
} else if (email.validity.tooShort) {
// 値が短すぎれば
emailError.textContent = `メールアドレスは ${email.minLength} 文字以上にしてください。入力されたのは ${email.value.length} 文字です。`;
}
// `active` クラスを追加
emailError.className = "error active";
}
入力値を変えるたびに、それが有効なデータを含んでいるかをチェックします。その場合は表示されたエラーメッセージを削除します。データが無効の場合は、適当なエラーを表示する showError() を実行します。
フォームの送信を試すごとに、またデータが有効かチェックします。その場合はフォームの送信を許可します。そうでない場合、適当なエラーを表示する showError() を実行し、 preventDefault()でフォーム送信を停止します。
showError() 関数は、入力の validity オブジェクトのさまざまなプロパティを使ってエラーがどれかを決めて、適当なエラーメッセージを表示します。
こちらが実際の結果です(Play ボタンをクリックすると、MDN Playground でサンプルを実行し、ソースコードを編集してください)。
制約検証 API はフォーム検証を制御するための強力なツールであり、HTML および CSS のみで検証を行うよりもはるかにユーザーインターフェイスをコントロールできます。
組み込み API を使用しないフォーム検証
古いブラウザーやカスタムコントロールにおいて、制約検証 API を使用できない (または使用したくない)ことがあるでしょう。このような場合でも、フォームを検証するために JavaScript が使用できます。フォームを検証には、実際のデータの検証よりもユーザーインターフェイスの疑問が多くなります。
フォームを検証するために、あなたはいくつかの疑問を考えなければなりません。
- どのような検証を実施するべきか
-
どのようにデータを検証するかを決めなければなりません。文字列演算、型変換、正規表現など。これはあなた次第です。
- フォームが有効でない場合に何をするべきか
-
これは明らかにユーザーインターフェイスの問題です。フォームがどのように動作するかを決めなければなりません。どのような場合でもフォームのデータを送信しますか? エラー状態の入力欄を強調しますか? エラーメッセージを表示しますか?
- ユーザーが無効なデータを修正することをどのように支援できるか
-
ユーザーの不満を軽減するためには、ユーザーに入力内容の修正を案内するために、できるだけ多くの役立つ情報を提供することがとても重要です。 明確なエラーメッセージはもちろん、ユーザーが何を求められているか理解できるように前向きの提案をするべきです。 フォーム検証のユーザーインターフェイスの要件について深く知りたいのであれば、ぜひ読むべきである有用な記事があります(英語)。
制約検証 API を使用しない例
説明のために、前回の例の制約検証 API を使用しない簡略版を次に示します。
ご覧の通り、 HTML はほとんど同じであり、HTML の検証機能を取り除いただけです。
<form>
<p>
<label for="mail">
<span>メールアドレスを入力してください:</span>
</label>
<input type="text" id="mail" name="mail" />
<span id="error" aria-live="polite"></span>
</p>
<button>送信</button>
</form>
同様に、 CSS も大きく変更する必要はありません。 :invalid 擬似クラスから実クラスへの変更と、属性セレクターの使用を避けただけです。
body {
font: 1em sans-serif;
width: 200px;
padding: 0;
margin: 0 auto;
}
form {
max-width: 200px;
}
p * {
display: block;
}
input {
appearance: none;
width: 100%;
border: 1px solid #333333;
margin: 0;
font-family: inherit;
font-size: 90%;
box-sizing: border-box;
}
/* これは無効なフィールド向けのスタイルです */
input.invalid {
border: 2px solid #990000;
background-color: #ffdddd;
}
input:focus.invalid {
outline: none;
/* キーボードのみを使用するユーザーが、フォーカス時に見ることができることを保証します。 */
border-style: dashed;
}
/* これはエラーメッセージ向けのスタイルです */
#error {
width: 100%;
font-size: 80%;
color: white;
background-color: #990000;
border-radius: 0 0 5px 5px;
box-sizing: border-box;
}
.active {
padding: 0.3rem;
}
JavaScript コードでは大きな変更があり、多くの面倒な作業が必要です。
const form = document.querySelector("form");
const email = document.getElementById("mail");
const error = document.getElementById("error");
// HTML 仕様上の電子メール検証用の正規表現
const emailRegExp = /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-z\d-]+(?:\.[a-z\d-]+)*$/i;
// メールアドレスが有効であるかをチェック
const isValidEmail = () => {
const validity = email.value.length !== 0 && emailRegExp.test(email.value);
return validity;
};
// email 入力のクラスを有効かどうかで更新
const setEmailClass = (isValid) => {
email.className = isValid ? "valid" : "invalid";
};
// エラーメッセージと表示の有無を更新
const updateError = (isValid) => {
if (isValid) {
error.textContent = "";
error.removeAttribute("class");
} else {
error.textContent = "I expect an email, darling!";
error.setAttribute("class", "active");
}
};
// input イベントを処理してメールの有効性を更新
const handleInput = () => {
const validity = isValidEmail();
setEmailClass(validity);
updateError(validity);
};
// メールアドレスが無効な場合にエラーを表示させるフォーム送信を処理
const handleSubmit = (event) => {
event.preventDefault();
const validity = isValidEmail();
setEmailClass(validity);
updateError(validity);
};
// これで検証制約を再構築できます。
// CSS 擬似クラスに頼っていないため、 email フィールドに
// 有効/無効のクラスを明示的に設定する必要があります。
const validity = isValidEmail();
setEmailClass(validity);
// これは、ユーザーがフィールドに入力したときに現れるものを定義します。
email.addEventListener("input", handleInput);
// これは、ユーザーがデータを送信しようとしたときに現れる内容を定義します。
form.addEventListener("submit", handleSubmit);
結果は以下のようになります。
ご覧の通り、自分でで検証システムを構築するのは大変なことではありません。難しいのはクロスプラットフォームで、かつ作成するであろうあらゆるフォームで使用できる汎用的なものにすることです。フォーム検証を行うために利用できる、Validate.js のような多くのライブラリーがあります。
まとめ
クライアント側のフォーム検証は、カスタムスタイル設定やエラーメッセージには複雑な JavaScript を必要としませんが、ユーザーについては注意深く考えることが必要です。ユーザーが正しいデータを入力できるよう支援することを、常に忘れないでください。最後に、以下のことを必ず行ってください。
- 明確なエラーメッセージを表示してください。
- 入力形式については寛容になってください。
- どこでエラーが発生しているかを正確に示してください(特に大きなフォームで)。
フォームが正しく埋められたことをチェックしたら、送信することができます。 次のデータ送信でカバーします。