フォームとインタラクション - 仕様書に見るHTML(4)

検索、オンラインショップ、掲示板投稿など、インタラクティブなウェブのためにフォームは欠かせません。利用者がデータを入力してサーバーに送るというこの機能については、WWWの初期からさまざまな意見が出され、現在も改良が続けられてます。

1 WWW初期のインタラクション

1.1 サーチインデックスと問い合わせURI

初期のWWWにはフォームという概念はありませんでしたが、必要な情報を探すために利用者が「問い合わせ」を行う仕組みは用意されていました。この一種の検索ゲートウェイは「インデックス」と呼ばれ、問い合わせのためには、インデックスのURIにキーワードを '?' でつなぐという方法が採られました。キーワードは '+' で連結して複数指定することもできます。バーナーズ=リーによる説明文書に載っていた簡単な用例を示しましょう。

http://cernvm/FIND/?sgml+cms

問い合わせをURIの一部として行うということは、この検索結果自体がひとつのアドレスを持つことを意味します。バーナーズ=リーの説明によれば、It may be considered to be the hypertext address of a document which is the result of making the keyword search on the index. ということで、このURI自身をブックマークなどで再利用できるわけです。

通常はこの「インデックス」にアクセスすると、サーバーがISINDEXという要素を含むHTMLを送り返しました。この要素を見つけると、ブラウザは自動的にキーワード入力用のフィールドを表示していたのです。

1.2 HTMLの機能拡張案とフォーム

ウェブが普及しはじめるとすぐに、この「インデックス」だけにとどまらない「問い合わせフォーム」の必要性が明らかになってきました。1992年末ごろから盛んに意見が交わされ、これはHTMLの拡張版であるHTML+で実現すべきものとして設計が進められます。

結局HTML+は見送られたわけですが、1995年にRFC 1866として公開されたHTML2.0では、HTML+のフォームを少し簡潔にしたものが採用されました。これはほぼそのまま(ファイルの送信という機能を加えて)HTML3.2にも引き継がれ、現在利用されるフォームの基本となっています。

2 フォームとコントロール

2.1 フォームとは

フォームは日常的に用いられているので、その役割はよく知られていると思いますが、仕様書ではどう定義されているのかを確認しておきましょう。

An HTML form is a section of a document containing normal content, markup, special elements called controls (checkboxes, radio buttons, menus, etc.), and labels on those controls.(17.1)

コントロール」という言葉は、フォームのボタンやメニューなど、ユーザーが制御できるオブジェクトをさします(以前は「フォームフィールド」と呼ばれていたのが、WindowsやVBの影響か、HTML4になってこの呼称に変更されました)。一般のマークアップも含められるというのは、フォームモデルにはコントロールをどうやって配置、表現するかという「ユーザインターフェイス」の側面があること(そして、それをHTMLで制御することが念頭に置かれていたこと)を示しています。

ご存知の通り、フォーム全体はform要素で、コントロールはその機能によってinput, select, textareaなどの要素を用いて表現します。

2.2 ラジオボタンとメニューの初期値

入力コントロールの中で、ラジオボタンとメニューは複数の選択肢から1つないし複数の項目を選ぶという機能です。これらは利用者が選択するまでは初期状態であるわけですが、この初期状態の扱いがHTMLのバージョンで微妙に異なります。HTML4仕様書は、ラジオボタンについて

If no radio button in a set sharing the same control name is initially "on", user agent behavior for choosing which control is initially "on" is undefined. (17.2.1)

としており、select要素のメニューも同様に指定がない場合の初期値は未定義です。一方HTML2.0では、指定がない場合のラジオボタンについては the user agent must check the first radio button of the set initially、メニューについては The initial state has the first option selected と定めているため、ブラウザによって挙動の違いが生じています。

混乱を避けるためには、これらの選択肢についてはchecked属性、selected属性で初期値を設定しておくよう求められています。

Since user agent behavior differs, authors should ensure that each menu includes a default pre-selected OPTION.(17.6.1)

こうしてみると、ラジオボタンとメニューの役割は共通する部分が多いことが分かります。実際、初期のフォームの案には、いずれもselect要素を用い、type属性を

type (radio|single|multiple) #REQUIRED

と定義することで使い分けるというアイデアがありました。なんだかこちらの方が合理的であるような感じもしてきますね。

3 フォームデータの送信

3.1 送信データと送信先

入力を終えた利用者がtype="submit"とされたinput要素を使って送信を指示すると、ブラウザは、フォーム内の有効なデータを、form要素のaction属性に記述されたURIに送ります。

A successful control is "valid" for submission. Every successful control has its control name paired with its current value as part of the submitted form data set.(17.13.2)

ここでsuccessful controlという訳しにくい用語が使われていますが、原義を尊重して「目的にかなったコントロール」としておきましょう。選択されたラジオボタンやメニュー、チェックがついたチェックボックスなどが「目的にかなった」もので、resetボタンやdisabled属性がセットされたコントロールは除外されます。また、submitボタンは送信指示に利用されたものだけが「目的にかなった」として対象になります(したがって、submitボタンを複数用意して使い分けることが可能です)。

ユーザーの指示を受け、ブラウザは次の手順でデータを処理します。

  1. 「目的にかなったコントロール」を確認する
  2. それらのコントロールの、名前と現在値をペアにしたデータセットをつくる
  3. データセットを送信用にエンコードする
  4. action属性で指定されたURIに、method属性のメソッドを使って送信する

ここで、4.のメソッドと3.のエンコードについて、仕様書をもとに説明しておきましょう。

3.2 フォーム送信のメソッド

method属性値には、getpostの2種類があります。これらはいずれもHTTPの要求メソッドに対応するものです。まずgetによる送信の定義から。

With the HTTP "get" method, the form data set is appended to the URI specified by the action attribute (with a question-mark ("?") as separator) and this new URI is sent to the processing agent. (17.13.1)

どこかで目にした仕様ですね。そう、これはWWWの初期から使われている、「インデックス」への問い合わせと基本的に同じ機能なのです。それに対してpostメソッドは

With the HTTP "post" method, the form data set is included in the body of the form and sent to the processing agent.(17.13.1)

となっており、データはURIには付加されません。

この2つの使い分けについて、仕様書は次のように説明しています。

The "get" method should be used when the form is idempotent (i.e., causes no side-effects). Many database searches have no visible side-effects and make ideal applications for the "get" method.(17.13.1)

このidempotentという難しい単語は、ある関数を何重に適用しても結果が同じであることを示す数学用語です。HTTP1.1の仕様書でも用いられており、そこでは the side-effects of N > 0 identical requests is the same as for a single request と記されています。つまり、リクエストによってデータベースなどに変化を生じず、またその結果も一定であるということ指しているわけですね。これは、問い合わせ文字列を含むURIをブックマークしたりアンカーにした場合、(少なくとも短期的には)毎回同じドキュメントが得られると考えてよいことを意味しています。

一方、postは「副作用のある」メソッドです。

If the service associated with the processing of a form causes side effects (for example, if the form modifies a database or subscription to a service), the "post" method should be used.

(17.13.1)

postとは「投稿する」ということですから、掲示板のフォームなどはまさにpostの出番です。投稿の結果、その内容が表示に反映され、投稿の前とは異なる「副作用」を生じています。

3.3 送信データのエンコード

getメソッドでのフォームデータはURIの一部になるため、URIで使えない文字を送る場合は何らかの変換が必要です。これがいわゆる「URLエンコード」で、次の手順で行われます。

  1. スペース文字を '+' で置き換える
  2. URIで使えない文字を、%HHという形に変換する(HHはその文字のASCIIコードの16進表記)。日本語のような2バイト文字は1バイトずつ変換。
  3. コントロール名と値のペアを '=' でつなぐ
  4. それぞれのペアを '&' で連結する

こうして「神崎」が「%90_%8D%E8」という具合になり、action属性で指定したURIに送られます。CGIなどは、この変換文字列を逆の順序でデコードし、利用者の入力したデータ名と値を取得するわけです。

postの場合はURIで使えない文字という制約はありませんが、form要素のenctype属性で特別な方法を指定しない限り、同じエンコードを施されたデータが送信されます。したがって、受信アプリケーションは、getの場合と同じ手法でデータをデコードして利用することができます。

なお、input要素にtype="file"を指定してファイルを送信する場合は、URLエンコードは非効率なのでenctype属性を"multipart/form-data"とし、MIMEのマルチパートが用いられます。

4 より高度で利用しやすいフォームへ

4.1 HTML2.0/3.2のフォームに不足しているもの

フォームを用いたデータベース管理やウェブアプリケーションの発達、またさまざまな環境での利用を考えると、HTML2.0/3.2のフォームは十分とはいえません。これに続くフォームについて検討したDesign Issues for HTML Formsなどの文書では、主に次のような点が改善点としてあげられています。

  • 入力値や文字コードをチェックするためのメカニズム
  • コントロールとラベルとの関連づけ
  • 関連するコントロールのグループ化
  • キーボードによる操作性の向上
  • より豊富で機能的なユーザインターフェイス

最初の4点はHTML4のフォームモデルに取り込まれ、以下のような拡張が行われました(最後の点を含めたさらに根本的な再設計として、XForms仕様の検討が進められています=2003年10月にW3C勧告)。

4.2 データのチェックと組み込みイベント

入力データを数字に限りたい、あるいは特定の範囲に限定したいという場合、データの整合性をサーバー側でチェックするのでは効率がよくありません。この検証をクライアント側スクリプトで行うという考えは早い段階から提起されていましたが、HTML4でscript要素が導入され、フォームのコントロールにもユーザーイベントを経由してスクリプトを起動する属性が組み込まれました。

データチェックのために使えるイベント属性としては、onchangeが代表的です。

The onchange event occurs when a control loses the input focus and its value has been modified since gaining focus. This attribute applies to the following elements: INPUT, SELECT, and TEXTAREA. (18.2.3)

コントロールの値が変更されて、利用者が別の操作をしようとしたときにこのイベントが送られるので、このタイミングでスクリプトを呼んで整合性をチェックさせるわけです。仕様書では

<INPUT NAME="num"
 onchange="if (!checkNum(this.value, 1, 10)) 
   {this.focus();this.select();}
   else {thanks()}"
 VALUE="0">

という例を掲載して、データの値によって再入力を求めたり、お礼を表示したりできることを示しています(この例のスクリプトは、もう少し手を加えないときちんと動かないのですが)。

文字コードをチェックする機能としては、form要素の属性としてaccept-charsetが追加されました。サーバーが処理できる文字コードを記述し、

User agents may advise the user of the value of the accept-charset attribute and/or restrict the user's ability to enter unrecognized characters. (17.3)

ということになっていますが、ブラウザではあまり実装されていないようです。

4.3 ラベルとコントロールの関連づけ

通常ボタン、メニューはそのvalue属性値などを画面表示しますが、他のコントロールはそのままでは「名無しさん」です。仕様書はこの点について

For those controls that have implicit labels, user agents should use the value of the value attribute as the label string. (17.9)

としているものの、なかなかそううまくは行きません。そこで、ラジオボタンや入力フィールドには「ラベル」と呼ばれる説明を加えます。しかし、ラベルがどのコントロールに対応するのかは必ずしも明確ではありません。HTML4では、label要素を用いて、ラベルとコントロールを結びつけることができるようになりました。

The LABEL element may be used to attach information to controls. Each LABEL element is associated with exactly one form control. (17.9.1)

label要素を用いると、ラベルをクリックしてもコントロールが反応するようになり、一般のアプリケーションのダイアログに近い動作となります。

関連づけのためには、label要素のfor属性に対象コントロールのid属性の値を記述します。

<label for="fname">First Name: </label>
<input type="text" name="sirname" id="fname">

また、label要素の内容にコントロールを含めて暗黙的に結びつける方法もあります。

<label>First Name: <input type="text" name="sirname"></label>

後者の方が簡単ですが、テーブルなどを用いてフォームを整形する場合、label要素がセルをまたぐことはできないので注意が必要です。

4.4 コントロールのグループ化

関連するコントロールのグループ化のためには、fieldset要素タイプが導入されました。

Grouping controls makes it easier for users to understand their purpose while simultaneously facilitating tabbing navigation for visual user agents and speech navigation for speech-oriented user agents. The proper use of this element makes documents more accessible. (17.10)

グループ化したコントロールには、legend要素でキャプションを与えます。

HTML4のform要素タイプの内容モデルは (%block;|SCRIPT)+ -(FORM) なので、input要素などのコントロールを直接記述することはできません。fieldset要素は%block;に含まれており、これを利用すればパラグラフでもないのにp要素としてマーク付けするという不自然さを避けることもできます(仕様書17.10の例ではformタグの後に<P>と記述されていますが、これは直後にfieldsetがあるため<P></P>と解釈されてしまい、無意味です)。

メニューを階層化するoptgroup要素も、コントロールのグループ化の一種と考えていいでしょう。主要なブラウザが対応しはじめており、メニューの使い勝手の向上が期待されます。

4.5 キーボードとアクセシビリティ

フォームのコントロールやリンクのアンカーなど、HTMLではなんらかの操作対象となる要素があります。これらに対して指示を与えるには、まずその要素に「フォーカス」をあてます。

In an HTML document, an element must receive focus from the user in order to become active and perform its tasks. For example, users must activate a link specified by the A element in order to follow the specified link. Similarly, users must give a TEXTAREA focus in order to enter text into it. (17.11)

キーボードによる操作を容易にするには、このフォーカスの方法に柔軟性を持たせることが必要です。HTML4では、このためにtabindexaccesskeyという2つの属性が導入されました。

tabindex属性は、その名のとおりTABキーによるフォーカス移動の順番を指定するものです。

This attribute specifies the position of the current element in the tabbing order for the current document. This value must be a number between 0 and 32767. User agents should ignore leading zeros. (17.11.1)

TABキーによるフォーカスの順序制御は、多くのアプリケーションのダイアログで使われている機能であり、うまく利用するとフォームを使いやすくできるでしょう。ただし、tabindex属性の順序は利用者には明示されないので、適切に設定しないと直感に反する分かりにくいものとなる危険があります。また、フォーム内だけでなく文書全体での順序しか設定できないため、不用意に用いると、本文を読んでいないのにいきなりフォームに移動するということにもなりかねません。

accesskey属性は、フォーカス移動のためのショートカットキーを指定する働きを持ちます。

Pressing an access key assigned to an element gives focus to the element. (17.11.2)

これも利用者には設定されていることが直接は分からないので、それを示す文字を()で併記するか、スタイルシートで

*[accesskey]:after {content: "(" attr(accesskey) ")"}

といった情報を提供する必要があるでしょう。

ところで、このaccesskeyで示されたキーを操作した場合にブラウザがどんな動作をするのかは、かなり混乱しています。前の定義に続け、仕様書は

The action that occurs when an element receives focus depends on the element. For example, when a user activates a link defined by the A element, the user agent generally follows the link.... (17.11.2)

と述べています。TABキーでのアンカーの移動を考えれば分かるように、focusとactivateは異なる行為なのですが、ここではその区別がはっきりしません。さらに、続くa要素のaccesskey属性の例では

Typing this access key takes the user to another document, in this case, a table of contents.

とすっかりactivate扱いです。結果として、リンクに設定したaccesskeyは、Windows IEではフォーカスをあてるだけ、MozillaやiCabなどではリンク先への移動と、ブラウザによって異なる動作をするようになってしまいました。

キーボードによる操作の支援は、アクセシビリティ向上のためには重要な手段ですが、こうした実装上の混乱や、ブラウザ自体のメニューショートカットとの競合など、まだまだ課題がたくさん残っています。XFormsやCSS3での対応も含め、改善に期待したいところです。