データフォームの検証

サーバでは常にデータを検証するべきですが、追加のデータ検証を 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) Example of an error message with Firefox in French on an English page
Chrome 22 (Windows 7) Example of an error message with Chrome in French on an English page
Opera 12.10 (Mac OSX) Example of an error message with Opera in French on an English page

これらのメッセージの外見やテキストを変更するには、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 で、setCutomValidity() メソッドを呼び出します:

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

コントロールが満たしていない検証制約を (もしあれば) 表す、ローカライズされたメッセージ、またはコントロールが検証制約の候補ではない場合 (willValidatefalse) に空文字列、または要素の制約を満たす値です。

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 の要件について深く学びたいのでしたら、ぜひ読むべきである有用な記事があります:

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";
  }
});

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

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

リモート検証

場合によっては、リモートで検証を実行することが役に立つかもしれません。このタイプの検証は、アプリケーションのサーバ側で保存されている付加的なデータとユーザが入力したデータが結びつけられる際に必要になります。これの用途のひとつが、登録フォームでユーザ名を尋ねるときです。重複を避けるためにユーザ名が利用可能かを確認するには、ユーザにデータの送信を求めてからエラー状態のフォームを返すのではなく、AJAX リクエストを実施する方がスマートであす。

このような検証を実施するには、いくつか用心が必要なことがあります:

  • API やいくつかのデータを広く公開することが必要です。機微な情報が無いようにしてください。
  • ネットワークの遅延により、非同期で検証を行うことが必要です。検証が正常に行われない場合でもユーザが邪魔されないようにするために、UI でいくらか作業が必要になります。

おわりに

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

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

添付ファイル

ファイル サイズ 日時 添付者:
Error message with Chrome (Win7)
Example of an error message with Chrome in French on an English page
3043 バイト 2012-11-28 02:16:29 Jeremie
Error message with Firefox (Win7)
Example of an error message with Firefox in French on an English page
3007 バイト 2012-11-28 02:17:09 Jeremie
Error message with Opera (MacOS)
Example of an error message with Opera in French on an English page
3810 バイト 2012-11-28 02:17:51 Jeremie

Document Tags and Contributors

Contributors to this page: ethertank, yyss
最終更新者: ethertank,