HTML5 でフォームを大幅に改良

HTML5 Rocks

はじめに

フォームが重要視されることはそれほどありませんが、HTML5 では、フォームを作成するデベロッパー、フォームに入力するユーザーの両方にメリットをもたらす大幅な改良が加えられました。フォーム関連の新しい要素、属性、入力タイプ、ブラウザベースの検証、CSS3 スタイリングの手法、FormData オブジェクトにより、フォームの作成が簡単になり、楽しめるまでになっています。

ブラウザのサポート状況

記事執筆時点で、フォームと入力に関連するすべての新しい要素、属性、タイプに対するサポートは、ブラウザによって大きく異なっています。特定の機能をサポートしているブラウザの間でも、動作には大きな違いが見られます。しかし、HTML5 のフォームのサポート状況は急速に変化しており、着実に改善が進んでいます。こちらの表は、HTML5 のフォームのブラウザ サポート状況について詳細を記したもので、執筆時点の最新状況を反映しています。

There is even more up to date forms guidance on our new Web Fundamentals site.

更新の概要

新しい要素

HTML5 では、入力とフォームに関連する新しい要素が 5 つ加わりました。

要素 目的 メモ
progress タスク完了までの進行状況を表します。 progress 要素を使って、アップロード中のファイルの進行状況を表すことができます。
meter 既知の範囲内にあるスカラー計測値を表します。 meter 要素を使って、温度や重量などの計測値を表すことができます。
datalist option 要素のセットを表します。input の新しい list 属性と組み合わせてプルダウン メニューを作成するときに使用できます。 datalist が関連付けられている input にフォーカスが移動すると、プルダウン メニューが表示され、datalist からの値が表示されます。
keygen キー ペアの生成用のコントロールです。 フォームの送信時、プライベート キーはローカルのキーストアに格納され、パブリック キーはサーバーに送信されます。
output 計算結果を表示します。 2 つの input 要素の値の合計を表示するなどの用途があります。

新しい入力タイプ

HTML5 では、新しい入力タイプが 13 個加わりました。これらの入力タイプは、サポートされていないブラウザではテキスト入力として表示されます。

入力タイプ 目的 メモ
tel 電話番号を入力します。 tel では特定の構文に従った入力は必須とされないので、特定の形式で入力されるようにする場合は、pattern または setCustomValidity() を使って追加の検証を行うことができます。
search 検索するテキストを入力するようユーザーに促します。 searchtext の違いは主にスタイル上のものです。入力タイプ search を使用すると、そのプラットフォームの検索フィールドに合わせて入力フィールドのスタイルが指定されることがあります。
url 単独の URL を入力します。 url は、単独の絶対 URL を入力するためのもので、多様な値に対応します。
email 単独のメール アドレスまたはメール アドレスのリストを入力します。 multiple 属性が指定されている場合は、複数のメール アドレスをカンマで区切って入力できます。
datetime 日付と時間(タイムゾーン UTC)を入力します。
date 日付を入力します(タイムゾーンなし)。
month 年月を入力します(タイムゾーンなし)。
week 年と週番号を入力します(タイムゾーンなし)。 たとえば、2011 年の 5 週目の場合は 2011-W05 という形式になります。
time 時間、分、秒、小数点以下の秒を入力します(タイムゾーンなし)。
datetime-local 日付と時間を入力します(タイムゾーンなし)。
number 数値を入力します。 有効な値は浮動小数点数(リンク先は英語)です。
range 数値を入力します。ただし、number と違って数値自体は重要ではありません。 この入力タイプがサポートされているほとんどのブラウザでは、範囲コントロールはスライダーで実装されます。
color カラーウェル コントロールから色を選択します。 値には、#ffffff など有効な小文字のカラー コード(リンク先は英語)を指定する必要があります。

新しい入力属性

HTML5 では、入力とフォームの要素にいくつかの新しい属性も加わりました。

属性 目的 メモ
autofocus ページの読み込み時に要素の入力内容にフォーカスを移動します。 autofocus は input、select、textarea、button で使用できます。
placeholder ユーザーに入力データの種類に関するヒントを示します。 要素にフォーカスが移動しユーザーがデータを入力するまで、プレースホルダの値は軽量テキストで表示されます。この属性は input と textarea で指定できます。
form 入力要素が属するフォームを 1 つまたは複数指定します。 form 属性を使用すると、form 要素内だけでなくページのどの場所にも入力要素を配置できます。また、1 つの入力要素を複数のフォームに関連付けることができます。
required 要素が必須であることを示す属性です。ブール値をとります。 required 属性は、カスタム JavaScript を使わないでブラウザベースの検証を行うときに便利です。
autocomplete フィールドで、ブラウザがユーザーの入力履歴を基にオートコンプリートや事前入力を行わないように指定できます。 autocomplete 属性は、クレジットカード番号や使い捨てパスワードなど、オートコンプリートを望まないフィールドに使用できます。デフォルトでは autocompleteon の状態になります。無効にするには off に設定します。
pattern 要素の値を正規表現と照合します。 pattern を使用する際は、title の値も指定して、想定されるパターンの説明をユーザーに示す必要があります。
dirname フォームのコントロールの方向を送信します。 たとえば、ユーザーが右から左の方向でテキスト データを入力し、入力要素に dirname 属性が含まれていた場合、右から左の方向を示す情報が入力値と一緒に送信されます。
novalidate form 要素で指定された場合に、フォーム送信の検証を無効にします。
formaction form 要素に指定された action 属性を上書きします。 この属性は input 要素と button 要素でサポートされています。
formenctype form 要素に指定された enctype 属性を上書きします。 この属性は input 要素と button 要素でサポートされています。
formmethod form 要素に指定された method 属性を上書きします。 この属性は input 要素と button 要素でサポートされています。
formnovalidate form 要素に指定された novalidate 属性を上書きします。 この属性は input 要素と button 要素でサポートされています。
formtarget form 要素に指定された target 属性を上書きします。 この属性は input 要素と button 要素でサポートされています。

FormData オブジェクト

XMLHttpRequest に加えられた変更の 1 つに、FormData オブジェクトの導入があります(リンク先はいずれも英語)。FormData オブジェクトを使用すると、Key-Value ペアのセットと(必要であれば)ファイルを作成して、XMLHttpRequest で送信することができます。この手法を使用する場合のデータの送信形式は、form で submit() メソッドを使って multipart/form-data エンコード形式のデータを送信した場合と同じになります。

FormData を使用すると、JavaScript を使って簡単に HTML 形式のフォームを作成し XMLHttpRequest.send() で送信することができます。簡単な例を次に示します:

var formData = new FormData();
formData.append("part_num", "123ABC"); 
formData.append("part_price", 7.95);
formData.append("part_image", somefile)

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://some.url/");  
xhr.send(formData);

FormData を使って、送信前の既存のフォームにデータを追加することもできます。

var formElement = document.getElementById("someFormElement");
var formData = new FormData(formElement);
formData.append("part_description", "The best part ever!");

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://some.url/");
xhr.send(formData);

ブラウザベースの検証

正直に言うとフォーム データの検証は退屈な作業ですが、避けて通ることはできません。最近のクライアントサイドのフォーム検証のやり方としては、カスタム JavaScript コードを作成するかライブラリを使って、フォーム送信前に入力内容が有効かどうかや必須フィールドに漏れがないかどうかを確認します。

requiredpattern といった新しい入力属性を CSS 擬似クラス セレクタと組み合わせて使用すると、チェック内容の記述やユーザーへのフィードバックの表示が簡単になります。その他の高度な検証手法を使って、JavaScript によりカスタムの有効性ルールやメッセージを設定したり要素が無効かどうかやその理由を判定したりすることもできます。

required 属性

required 属性がある場合は、フォームの送信時点でフィールドに値が指定されている必要があります。下記はメール アドレスを必須とする入力フィールドの例です。この指定方法により、フィールドに値が指定されることと、値が有効なメール アドレスであることを必須にできます(有効なメール アドレスの定義についてはこちら(英語)をご覧ください)。

<input type="email" id="email_addr" name="email_addr" required />

pattern 属性

pattern 属性では、入力フィールドの検証に使用される正規表現を指定します。下記の例は、部品番号を入力する必須のテキスト入力フィールドを表しています。部品番号には、アルファベットの大文字 3 文字とそれに続く 4 桁の数字を入力する必要があります。requiredpattern を使用すると、フィールドに値が指定されることと、値が部品番号の正しい形式に一致することを必須にできます。ユーザーがフィールドにマウスを合わせたときには、title 属性に指定したメッセージが表示されます。

<input type="text" id="part" name="part" required pattern="[A-Z]{3}[0-9]{4}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>

上記の例から発展して、無効な部品番号が入力されたときに入力フィールドを赤枠で示すこともできます。これには、次の CSS を追加して、値が無効なときに入力フィールドを赤枠で囲むように指定します。

:invalid {
  border: 2px solid #ff0000;
}

formnovalidate 属性

formnovalidate 属性は、input または button 要素で使用できます。この属性がある場合、フォーム送信の検証は無効になります。下記は、[送信] ボタンでフォームを送信する場合は有効な入力を必須とし、[保存] ボタンで送信する場合は有効な入力を必須としない例です。

<input type="text" id="part" name="part" required pattern="[A-Z]{3}[0-9]{4}"
       title="Part numbers consist of 3 uppercase letters followed by 4 digits."/>
<input type="submit" formnovalidate value="Save">
<input type="submit" value="Submit">

Constraint Validation API

Constraint Validation API(リンク先は英語)は、カスタム検証の処理に使える強力なツールです。この API を使用すると、カスタム エラーを設定する、要素が有効かどうかをチェックする、要素が無効な理由を特定するなどの処理を行うことができます。下記は、2 つのフィールドの値が一致しない場合にカスタム エラーを表示する例です。

<label>Email:</label>
<input type="email" id="email_addr" name="email_addr">

<label>Repeat Email Address:</label>
<input type="email" id="email_addr_repeat" name="email_addr_repeat" oninput="check(this)">

<script>
function check(input) {
  if (input.value != document.getElementById('email_addr').value) {
    input.setCustomValidity('The two email addresses must match.');
  } else {
    // input is valid -- reset the error message
    input.setCustomValidity('');
  }
}
</script>

総合的な例

下記は、さまざまな入力タイプ、フォームの検証、CSS セレクタ/スタイリングを組み合わせて作成した予約申し込みフォームの例です。

$99.00

このフォームの HTML と JavaScript は次のようになります:

<form oninput="total.value = (nights.valueAsNumber * 99) + 
 ((guests.valueAsNumber - 1) * 10)">

  <label>Full name:</label>
  <input type="text" id="full_name" name="full_name" placeholder="Jane Doe" required>

  <label>Email address:</label>
  <input type="email" id="email_addr" name="email_addr" required>

  <label>Repeat email address:</label>
  <input type="email" id="email_addr_repeat" name="email_addr_repeat" required 
   oninput="check(this)">

  <label>Arrival date:</label>
  <input type="date" id="arrival_dt" name="arrival_dt" required>
  
  <label>Number of nights (rooms are $99.00 per night):</label>
  <input type="number" id="nights" name="nights" value="1" min="1" max="30" required>

  <label>Number of guests (each additional guest adds $10.00 per night):</label>
  <input type="number" id="guests" name="guests" value="1" min="1" max="4" required>

  <label>Estimated total:</label>
  $<output id="total" name="total">99</output>.00
  <br><br>

  <label>Promo code:</label>
  <input type="text" id="promo" name="promo" pattern="[A-Za-z0-9]{6}" 
   title="Promo codes consist of 6 alphanumeric characters.">

  <input type="submit" value="Request Reservation" /> 
</form>

<script>
function check(input) {
  if (input.value != document.getElementById('email_addr').value) {
    input.setCustomValidity('The two email addresses must match.');
  } else {
    // input is valid -- reset the error message
    input.setCustomValidity('');
  }
}
</script>

このフォームのコードに付属する CSS は次のようになります:

:invalid { 
  border-color: #e88;
  -webkit-box-shadow: 0 0 5px rgba(255, 0, 0, .8);
  -moz-box-shadow: 0 0 5px rbba(255, 0, 0, .8);
  -o-box-shadow: 0 0 5px rbba(255, 0, 0, .8);
  -ms-box-shadow: 0 0 5px rbba(255, 0, 0, .8);
  box-shadow:0 0 5px rgba(255, 0, 0, .8);
}

:required {
  border-color: #88a;
  -webkit-box-shadow: 0 0 5px rgba(0, 0, 255, .5);
  -moz-box-shadow: 0 0 5px rgba(0, 0, 255, .5);
  -o-box-shadow: 0 0 5px rgba(0, 0, 255, .5);
  -ms-box-shadow: 0 0 5px rgba(0, 0, 255, .5);
  box-shadow: 0 0 5px rgba(0, 0, 255, .5);
}

form {
  width:300px;
  margin: 20px auto;
}

input {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  border:1px solid #ccc;
  font-size:20px;
  width:300px;
  min-height:30px;
  display:block;
  margin-bottom:15px;
  margin-top:5px;
  outline: none;

  -webkit-border-radius:5px;
  -moz-border-radius:5px;
  -o-border-radius:5px;
  -ms-border-radius:5px;
  border-radius:5px;
}

input[type=submit] {
  background:none;
  padding:10px;
}

関連資料

Comments

0