フォームデータの検証

This translation is incomplete. Please help translate this article from English

データをサーバへ送信する前に、必須のフォームコントロールが記入され、すべてのフォームコントロールが正しい書式で記入されていることを保証することが重要です。このクライアント側フォーム検証validationは、送信されるデータが様々なフォームコントロールで指定されている要件を満たしていることを保証します。この記事では、クライアント側フォーム検証の基本概念と例を紹介します。

クライアント側検証は最初のチェックであり、ユーザーの使い勝手を良くするために重要な機能です。無効なデータがサーバーに送られて拒否される前に捕らえて修正を要求することで、サーバー側検証のためにサーバーへの往復によって時間を浪費することを防ぎます。サーバー側検証も、サーバーへ送信されたデータをチェックし、不正または悪意のあるデータを確実に拒否するためになお必要です。サーバー側検証はこのガイドでは説明しません。

このチュートリアルはクライアント側のネイティブおよびスクリプトによる検証を紹介します。何が可能か、何が必要かを概観するだけです。このチュートリアルよりも詳しい情報は、制約検証ガイドを参照してください。

前提知識: コンピューターリテラシー、適度な HTMLCSSJavaScript の理解。
目的: フォーム検証とは何か、なぜ重要なのか、どのように実装するのかを理解すること。

フォーム検証とは何か

有名なサイトの登録フォームに行き、データを求められている書式で入力しなかいと、フィードバックがあることに気づくでしょう。次のようなメッセージが表示されます。

  • 「このフィールドは必須です」 (このフィールドが空欄にできない場合)
  • 「電話番号は XXX-XXXX の形式で入力してください」 (フォームが3桁の番号の後にダッシュがあり、その後に4桁の番号があるパターンを強制する場合)
  • 「有効なメールアドレスを入力してください」 (入力が適切な、 "somebody@example.com" の形ではない場合)
  • 「パスワードは8文字から30文字の間で、1文字以上の大文字、記号、数字を含む必要があります。」 (長さが minlength 属性よりも短いか、maxlength 属性よりも長いか、内部の pattern 属性または JavaScript で行われる正規表現に一致しない場合)

これはフォーム検証と呼ばれます。データを入力したとき、ブラウザー、またはウェブアプリケーションは、そのデータが正しい書式であり、様々な検証に関する属性によって設定された制約に合っているかをチェックします。情報が正しい書式であれば、ユーザーエージェント (およびアプリケーション) はデータをサーバーに送信することができ、 (ふつうは) データベースに保存されます。正しい書式でなければ、何を修正する必要があるかを説明するメッセージを表示します。

私たちはできるだけ簡単にフォームを埋めてもらいたいわけですが、なぜフォームを検証する必要があるのでしょうか?理由は主に三つあります。

  • 正しいデータを正しい書式で入力してほしい。ユーザーのデータが誤った形式で格納されたり、ユーザーが正しい情報を入力しなかったり、省略したりすると、アプリケーションが正しく動作しないからです。
  • ユーザーのアカウントを保護したい。ユーザーに安全なパスワードを入力させることで、アカウント情報を保護しやすくなります。
  • 自分たちを守りたい。悪意のあるユーザーが保護のないフォームを悪用して、そのフォームを一部に持つアプリケーションに危害を加える方法がたくさんあります。 (ウェブサイトセキュリティを参照してください)。
  • 警告: クライアントからサーバーに渡されたデータを信用しないでください。フォームが正しく検証を行い、クライアント側で悪意のある入力を防いでいるとしても、悪意のあるユーザーはネットワークリクエストを改ざんすることができます。

様々な種類のフォーム検証

ウェブで見かけるフォーム検証には二つの種類があります。

  • クライアント側検証は、データがサーバーへ送信される前にブラウザー内で行われる検証です。これはすぐに反応を返せるので、サーバー側検証よりもユーザーに親切です。これはさらに分類できます。
    • JavaScript 検証は JavaScript を使ってコーディングされるものです。これは完全にカスタマイズ可能です。
    • 内蔵フォーム検証は、 HTML5 のフォーム検証機能を使用します。これは一般に JavaScript を必要としません。内蔵フォーム検証はパフォーマンスは良いのですが、 JavaScript ほどカスタマイズすることはできません。
  • サーバー側検証は、データが送信された後に、サーバー側で行われる検証です。サーバー側のコードは、データベースにデータを保存する前の検証に使用されます。データの検証に失敗すると、クライアントにレスポンスを送り、ユーザーに何が間違っているかを伝えます。サーバー側検証は、フォーム全体が送信されるまでエラーが表示できないので、クライアント側検証ほどユーザーに親切ではありません。しかし、サーバー側検証は不正なデータに対するアプリケーションの最終防衛線です。主要なサーバー側フレームワークはデータを検証して無害化 (sanitizing) するか、安全化する機能を持っています。

ユーザーエクスペリエントとセキュリティのために、開発者はクライアント側とサーバー側の両方を組合せて使うべきです。クライアント側の検証はサーバー側の検証よりユーザーフレンドリーです。というのも、クライアント側の検証はフォーム全体の完了、送信、分析、および返信を求めておらず、フォームの完了時にエラーを返すからです。サーバー側の検証は必須だと考えられます。というのもクライアント側のエラーチェックはバイパスすることができ、またネットワークの要求は変更可能だからです。

内蔵フォーム検証の利用

HTML5 のフォーム制御の機能の一つにスクリプトに頼らない多くのユーザーデータの検証があります。これはフォーム要素の 検証属性(validation attributes) を使って行います。 検証属性は入力フォームのためのルールを指定できます。例えば、 required属性は値が入力されているべきかどうかを指定します。minlength 属性と maxlength属性はデータ長の最小値と最大値を検証します。min 属性と max属性は値の最小値と最大値を検証します。type 属性はその入力データが数値かEメールアドレスかそれ以外かを検証します。また、pattern 属性は、データが指定された正規表現にマッチするかどうかを検証します。これらがサポートされているブラウザでは、入力データをこの指定されたルールに基いて検証します。検証にパスすれば妥当で検証にパスしなければ妥当ではないと考えます。

要素が妥当な場合は、次のようになります。

  • 要素が CSS の :valid 疑似クラスに一致します。これにより、妥当な要素に特定のスタイルを適用することができます。
  • ユーザーがデータを送信しようとすると、ブラウザーは止めるものが他になければ(JavaScript など)、フォームを送信します。

要素が不正なときは、次のようになります。

  • 要素が CSS の :invalid 疑似クラスに一致します。これにより、不正な要素に特定のスタイルを適用することができます。
  • ユーザーがデータを送信しようとすると、ブラウザーはフォームをブロックしてエラーメッセージを表示します。

サーバーへの送信を中断するエラーがいくつかあります。次のものが含まれます: badInputpatternMismatchrangeOverflow またはunderFlowstepMismatch、フォームの制御の tooLong またはtooShorttypeMismatch、と 必要とされた値のvalueMissing、また customErrorも含まれる。

入力要素の制約の検証 ― 簡単なものから

この節では、 <input> 要素を検証するために使用することができる幾つかの異なる HTML5 の機能を見てみます。

簡単な例から始めましょう。 ― 好きな果物をbanana(バナナ)かcherry(サクランボ)から選べる入力欄があるとします。単純なテキストの <input> とそれに合わせたラベル、送信の <button> から成ります。ソースコードは GitHub の fruit-start.html で、ライブサンプルは次の通りです。

<form>
  <label for="choose">banana と cherryのどちらが好き?</label>
  <input id="choose" name="i_like">
  <button>Submit</button>
</form>
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

始めるにあたって、ハードディスク内の新しいディレクトリに fruit-start.html のコピーを作成してください。

required 属性

required 属性は、使うのがもっとも簡単な HTML5 の検証機能です。 ― 入力欄を必須にしたい場合は、この属性を使用して要素をマークすることができます。この属性が設定されていると、入力欄が空欄の場合はフォームが送信されません。入力欄は不正だと扱われ、そして、エラーメッセージが表示されるでしょう。

以下のように、 required 属性を入力欄に追加しましょう。

<form>
  <label for="choose">banana と cherryのどちらが好き? (要入力)</label>
  <input id="choose" name="i_like" required>
  <button>Submit</button>
</form>

このサンプルファイルの中に含まれている 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 によって、入力が妥当でない場合には、入力を赤の破線で境界線を描きますが、入力が妥当な場合には、黒の直線で境界線を描きます。:required疑似クラスをに、要求された値があり、値が妥当でないときは背景にグラディエーションを追加します。つぎの例の動作を確認しましょう。

required属性をサポートしている要素にこの属性がある場合、その要素に値があるかないかによって、要素が :required 疑似クラスに一致するかどうかが決まります。もし <input> に値がなければ、 input:invalid 疑似クラスに一致します。そしてelement.validity.valueMissing制約検証constraint validation の間true を返します。

注意: よりよいユーザーエクスペリエンスのために、フォームのフィールドが必要なときにはユーザーに通知しましょう。これだけがよいユーザーエクスペリエンスというわけではありませんが、これは WCAG アクセシビリティ ガイドラインで求められています。また、あなたが本当に必要とする場合にのみ求めましょう。例えばあなたは誰かの性別などの情報は本当に必要でしょうか?

値のないままフォームを送信してみましょう。妥当ではない入力がどのようにフォーカスされるか注意しましょう。デフォルトのエラーメッセージ("Please fill out this field") が表示され、フォームの送信を阻止します。

正規表現での検証

もう一つとてもよく使われる機能は pattern 属性で、値として正規表現を取ります。正規表現 (regex) はテキスト文字列の中の文字の組み合わせに一致させるために使うことができ、フォームの検証には理想的です (JavaScript と同様に様々な利用ができます) 。正規表現はとても複雑であり、この記事では包括的な説明をしようとはしていません。

正規表現はかなり複雑です。このモジュールでは正規表現の全てを説明する意図はありません。いくつかの例を挙げますのでどのように動作するか基本的なアイディアを把握してください。

  • aa の1文字に一致する (baa などではない)。
  • abca と、その次の b と、その次の c の並びに一致する。
  • ab?caと、その次にひとつだけbがあるかないかと、その次の cの並びに一致する ( ac or abc)
  • ab*caと、その次に任意の数の bが続き、その次に cのある並びに一致する。 ( ac , abc, abbbbbc, 等)
  • a|b — 一文字の a または b に一致する
  • abc|xyz —  abc の並びまたは xyz の並びに一致する。これは abcxyzay などには一致しない。
  • 正規表現には多くの組合せがあるので例はここまでとする。

使用例を実装しましょう。HTML を更新して pattern 属性を追加しましょう:

<form>
  <label for="choose">banana と cherryのどちらが好き?</label>
  <input id="choose" name="i_like" required pattern="banana|cherry">
  <button>Submit</button>
</form>
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

この例では、 <input> 要素は "banana" または "cherry" という2つの文字列値のうち1つを受け付けます。

この時点で、 pattern 属性の中の値を以前に見たいくつかの例と同じ値に変更してみて、入力欄が有効になるように入力する値がどのように影響するかを確認してください。自分で考えた値も書いてみて、どのようになるか確認しましょう。果物に関する値を可能にすれば、例が分かりやすくなります。

もし <input> の空ではない値が正規表現パターンに一致しなかった場合、この input:invalid 疑似クラスに一致します。このフォームが送信されたとき、theInput.validity.patternMismatchtrue を返します。

この patternMismatch が true を返すのは、その要素の値が指定されたパターンに一致しなかったときです。false を返すのはその逆です。trueのとき、サポートされているブラウザであれば、このフォームは送信されないでしょう。

メモ: <input> 要素の型によっては、検証のために pattern 属性が必要ないことがあります。例えば email 型を指定すると、入力された文字列を、妥当な形式のメールアドレス(または、 multiple 属性がある場合はコンマで区切られたメールアドレスのリスト)であることを確認する正規表現で検証します。他の例として、 url 型の入力欄は自動的に正しい形式の URL を要求します。

メモ: <textarea> 要素は pattern 属性に対応していません。

入力欄の長さの制約

あなたが、<input> または <textarea> によって作成して全てのテキストフィールドで文字数を制限したいときには minlength 属性と maxlength 属性が使用できます。フィールドが妥当ではない - (tooShort) - のは、それが値をもっており、その文字数が minlength の値より少ないか、文字数が maxlength の値より大きいか - (tooLong) です。

ブラウザはよくテキストフィールドに期待している以上に入力させないことがあります。maxlength を使ううえでのユーザーエクスペリエンスの点からいえば、制限された文字数以上の入力を許可しないことより、ユーザーに入力させてかつ入力文字数のフィードバックを提供しさらに編集できるままにしておくことでユーザーにその内容を短くさせることです。例えば、Twitterの文字入力の制限があります。これは JavaScriptと solutions using maxlength の組合せで実現できます。

入力欄の値に制約を加える

数値のフィールド (例えば <input type="number">)の場合、 min 属性と max 属性によって入力に制限を加えられます。もしそのフィールドの値が min 属性の値より少ないとき rangeUnderflow は true を返します。同様に、 max 属性より大きいときにはrangeOverflow が true を返します。どちらの場合でもそのフィールドは妥当ではありません。

他の例を見てみましょう。 fruit-start.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>Submit</button>
  </div>
</form>
  • ここで、 text フィールドには minlength 属性と maxlength 属性には6 を指定しています。これは banana「バナナ」と cherry「さくらんぼ」の文字数と同じです。より少い文字数を入力した場合、これは妥当ではないと表示され、制約検証上でelement.validity.tooShort は trueを返します。より多くの文字数の入力はほとんどのブラウザでは不可能ですが、element.validity.tooLong は true を返すでしょう。
  • またここでは、 number フィールドに min 属性で1 を max 属性で 10を指定しました。この範囲外の数字の入力は妥当ではないと表示されます。ユーザーは矢印を使ってこの範囲外の値に増減できませんが、ユーザーが数字を手入力した場合がありえます。制約検証は element.validity.rangeUnderflow または element.validity.rangeOverflow のどちらがtrueになりっます。 step 属性のデフォルトは 1です。これは3.2のような小数が妥当ではないことを意味しています。制約検証のstepMismatchtrue を返します。この数字が必要ではないので、値の除去が妥当です。そうでなければ stepMismatch または rangeUnderflow
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

div {
  margin-bottom: 10px;
}

例をライブで実行してみましょう。

メモ: <input type="number"> (及び range のような他の型)は step 属性を取ることもでき、入力コントロール(数値の増加・減少ボタンなど)を使用するときに上げ下げすることができる値の刻みを設定することができます。

サンプル全体

HTML の内蔵検証機能の使い方を示す例の全体を示します。

<form>
  <p>
    <fieldset>
      <legend>Do you have a driver's license?<abbr title="This field is mandatory" aria-label="required">*</abbr></legend>
      <input type="radio" required name="driver" id="r1" value="yes"><label for="r1">Yes</label>
      <input type="radio" required name="driver" id="r2" value="no"><label for="r2">No</label>
    </fieldset>
  </p>
  <p>
    <label for="n1">How old are you?</label>
    <!-- pattern 属性は number 型の入力欄を実装していないものの、 pattern
         属性には対応しているブラウザー向けの代替策として動作できます。
         なお、 pattern 属性に対応しているブラウザーでは、 number 型の入力欄
         で使用すると暗黙に失敗します。
         ここでは代替策としての使い方のみです。 -->
    <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" aria-label="required">*</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 address?</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>
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 #333;
  box-sizing: border-box;
}

input:invalid {
  box-shadow: 0 0 5px 1px red;
}

input:focus:invalid {
  box-shadow: none;
}

独自のエラーメッセージ

上記の例で見てきたように、ユーザーが不正なフォームを送信しようとするたびにブラウザーはエラーメッセージを表示します。このメッセージを表示する方法は、ブラウザーにより異なります。

これらの自動のメッセージには、2つの欠点があります。

  • CSS でメッセージの表示方法を変更するための標準的な方法がありません。
  • メッセージはブラウザーのロケールに依存しており、ある言語のページでエラーメッセージが別の言語で表示されることがあります。
フランス語版のブラウザーで英語のページを表示する
ブラウザー 表示
Firefox 17 (Windows 7) 英語ページでフランス語のエラーメッセージを Firefox で表示した例
Chrome 22 (Windows 7) 英語ページでフランス語のエラーメッセージを Chrome で表示した例
Opera 12.10 (Mac OSX) 英語ページでフランス語のエラーメッセージを Opera で表示した例

これらのメッセージの外見やテキストを変更するには、 JavaScript を使用しなければなりません。 HTML や CSS だけで変更する方法はありません。

HTML5 では、フォーム要素の状態を確認したりカスタマイズしたりするための制約検証 API を提供します。特に、エラーメッセージのテキストを変更できます。簡単な例を見てみましょう:

<form>
  <label for="mail">私にメールアドレスを教えてください。</label>
  <input type="email" id="mail" name="mail">
  <button>Submit</button>
</form>

JavaScript で setCustomValidity() メソッドを呼び出します。

const email = document.getElementById("mail");

email.addEventListener("input", function (event) {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I am expecting an e-mail!");
  } else {
    email.setCustomValidity("");
  }
});

注意: もしあなたが独自のメッセージを設定する場合、その値が妥当な場合には空文字列を設定しなければなりません。そうでなければ、そのフォームは何も送信できません。

JavaScript を使用したフォーム検証

内蔵のエラーメッセージを見かけを制御したい場合や、HTML5 のフォーム検証に対応していないブラウザーに対処したい場合は、 JavaScript を使用する必要があります。

HTML5 の制約検証 API

多くのブラウザーが制約検証 API に対応しています。この API は各フォーム要素で使用できる一連のメソッドやプロパティで構成されています。

制約検証 API のプロパティ

プロパティ 説明
validationMessage コントロールが合格していない制約検証 (もしあれば) を説明するローカライズ済みのメッセージです。またはコントロールが制約の検証の対象ではない場合 (willValidatefalse) または要素の値が制約に合格している場合は空文字列です。
validity 要素の検証状態を説明する ValidityState オブジェクトです。取りうる検証状態の詳細は記事を参照してください。
willValidate フォームが送信されるときに要素が検証される場合に true を返します。そうでない場合は false を返します。

制約検証 API のメソッド

メソッド 説明
checkValidity() 要素の値で妥当性の問題がない場合に true を返します。そうでない場合は false を返します。要素が不正である場合、このメソッドは要素で invalid イベントを発生させます。
HTMLFormElement.reportValidity() この要素の子コントロールが検証の制約を満足するのであれば true を返します。 false が返された場合、取り消し可能な invalid イベントがすべての無効な子に対して発行され、検証の問題がユーザーに報告されます。
setCustomValidity(message) 要素に独自のエラーメッセージを追加します。独自のエラーメッセージを設定すると、要素が不正であるとみなされる場合に指定したエラーが表示されます。これにより JavaScript で、標準の制約検証 API で提供されるもの以外の検証不合格状態を作り出すことができます。ユーザーに問題を報告する際に、メッセージが表示されます。

引数が空文字列である場合は、独自のエラーがクリアされます。

古いブラウザー向けには、制約検証 API に対応していないことを補うための Hyperform などのポリフィルを使用できます。すでに JavaScript を使用していますので、ポリフィルを使用することがウェブサイトやウェブアプリケーションの設計や実装での追加の負担にはなりません。

制約検証 API の使用例

独自のエラーメッセージを作成するために API を使用する方法を見ていきましょう。始めに、HTML です。

<form novalidate>
  <p>
    <label for="mail">
      <span>メールアドレスを入力してください。</span>
      <input type="email" id="mail" name="mail">
      <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <button>Submit</button>
</form>

このサンプルフォームでは、ブラウザーの自動検証を無効にするために novalidate 属性を使用しています。これで、検証を制御するためにスクリプトを使用できます。ただし、これは制約検証 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%;

  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;
 
  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("input", 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);

こちらが実際の結果です。

制約検証 API はフォーム検証を制御するための強力なツールであり、HTML および CSS のみで検証を行うよりもはるかにユーザーインターフェイスをコントロールできます。

組み込み API を使用しないフォーム検証

古いブラウザーやカスタムウィジェットにおいて、制約検証 API を使用できない (または使用したくない)ことがあるでしょう。このような場合でも、フォームを検証するために JavaScript が使用できます。フォームを検証には、実際のデータの検証よりもユーザーインターフェイスの疑問が多くなります。

フォームを検証するために、あなたはいくつかの疑問を考えなければなりません。

どのような検証を実施するべきか
どのようにデータを検証するかを決めなければなりません。文字列演算、型変換、正規表現など。これはあなた次第です。フォームのデータは常にテキストであり、スクリプトには常に文字列として渡されることを忘れないようにしてください。
フォームが妥当でない場合に何をするべきか
これは明らかにユーザーインターフェイスの問題です。フォームがどのように動作するかを決めなければなりません。どのような場合でもフォームのデータを送信しますか?エラー状態の入力欄を強調しますか?エラーメッセージを表示しますか?
ユーザーが不正なデータを修正することをどのように支援できるか
ユーザーの不満を軽減するためには、ユーザーに入力内容の修正を案内するために、できるだけ多くの役立つ情報を提供することがとても重要です。明確なエラーメッセージはもちろん、ユーザーが何を求められているか理解できるように前向きの提案をするべきです。フォーム検証のユーザーインターフェイスの要件について深く知りたいのであれば、ぜひ読むべきである有用な記事があります。

制約検証 API を使用しない例

これまでのことを説明するため、古いブラウザーでも動作するように前出のサンプルを作り直してみましょう。

<form>
  <p>
    <label for="mail">
        <span>メールアドレスを入力してください。</span>
        <input type="text" class="mail" id="mail" name="mail">
        <span class="error" aria-live="polite"></span>
    </label>
  <p>
  <!-- 一部の古いブラウザーでは button 要素で、 type 属性に明示的に
       submit を設定する必要があります。 -->
  <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%;

  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;
  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}
JavaScript

JavaScript コードでは大きな変更があり、多くの面倒な作業が必要です。

// 古いブラウザーで DOM ノードを選択する方法は少ない
const form  = document.getElementsByTagName('form')[0];
const email = document.getElementById('mail');

// 以下は DOM 内で次の兄弟要素にたどり着くための技です。
// これは容易に無限ループになることがあるため、危険です。
// 新しいブラウザーでは、element.nextElementSibling を使用しましょう。
let error = email;
while ((error = error.nextSibling).nodeType != 1);

// HTML5 仕様書より
const emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

// 多くの古いブラウザーは addEventListener メソッドをサポートしていません。
// 以下はこれを扱うためのシンプルな方法です。なお唯一の方法ではありません。
function addEvent(element, event, callback) {
  let previousEventCallBack = element["on"+event];
  element["on"+event] = function (e) {
    const 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 () {
  // ここで、フィールドが空かを確認しています (フィールドは必須入力ではありません)
  // 空でなければ、内容部が適切な電子メールアドレスかを確認します。
  const test = email.value.length === 0 || emailRegExp.test(email.value);

  email.className = test ? "valid" : "invalid";
});

// ユーザーがフィールドに入力したときに、何をするかを定義します。
addEvent(email, "input", function () {
  const 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 () {
  const 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";
  }
});

結果は以下のようになります。

ご覧の通り、自分でで検証システムを構築するのは大変なことではありません。難しいのはクロスプラットフォームで、かつ作成するであろうあらゆるフォームで使用できる汎用的なものにすることです。フォーム検証を行うために利用できる、多くのライブラリがあります。使用するのをためらうべきではありません。いくつか例を挙げます。

リモート検証

場合によっては、リモートで検証を実行することが有用かもしれません。この種の検証は、ユーザーが入力したデータをアプリケーションのサーバー側で保存している追加データに結び付ける際に必要になります。この利用例として、登録フォームでユーザー名を尋ねるときです。重複を避けるためには、ユーザーにデータを送信させてから、エラーを伴ったフォームを送り返すよりも、ユーザー名が利用できるかをチェックするために AJAX リクエストを実行する方がスマートです。

このような検証を実施するには、いくつか注意すべきことがあります。

  • API や一部のデータを広く公開することになります。微妙なデータでないことを確認してください。
  • ネットワークの遅延があるので、検証は非同期で行うことになります。検証が正しく行われなくてもユーザーはブロックされないので、正しいことを確認するために UI でいくらかの作業が必要になります。

まとめ

フォーム検証は複雑な JavaScript を必要としませんが、ユーザーについては注意深く考えることが必要です。ユーザーが正しいデータを入力できるよう支援することを、常に忘れないでください。最後に、以下のことを必ず行ってください。

  • 明確なエラーメッセージを表示してください。
  • 入力形式については寛容になってください。
  • どこでエラーが発生しているかを正確に示してください(特に大きなフォームで)。

このモジュール内の文書