« びばWindows Server | メイン | トランプたわぁ »

2005年09月29日

button は酷い! [DOM]

button は酷い。私はこの要素の故に何度も失望させられてきた。あまりの屑加減に、丸ごと放り投げたくなってくるほどだ。何が酷いのだろう。IE の実装状況だ。

テキストボタン

次のような単純なHTMLフォームがあったとする。 このボタンを押したとき、各ブラウザでの送信文字列は次のようになる。

<form action="index.cgi">
  <p>
    <button type="submit" name="command" value="1">
      ClickHere
    </button>
  </p>
</form>
GekkoやOperaの場合
command=1
Internet Explorerの場合
command=ClickHere

違いをよく見てみよう。Gekkoエンジンでは command という名前のキーに対し、value 属性の値、つまり 1 が割り当てられて送信されている。 それに対し IE は、なんと子のテキストノードを値として送信してしまうのだ!

参考までに、HTML4.01 の DTDを紐解いてみよう。

<!ATTLIST BUTTON
 value CDATA #IMPLIED  -- sent to server when submitted --
>

value 属性の値をサーバに sent(送信)するとある。明らかにGekkoの実装のほうが正しいことがわかる。

画像ボタン

さらに驚くべきことがある。HTML 4.01 では、次のような button 要素の使い方も許されている。

<form action="button.html">
 <p>
    <button type="submit" name="command" value="1">
      <img alt="ClickHere" src="./button.png" />
    </button>
  </p>
</form>

このようにすると、"./button.png" というURLのファイルをボタン画像として表示することができる。これを各ブラウザで送信してみよう。

GekkoやOperaの場合
command=1
Internet Explorerの場合
command=%3CIMG+alt%3DClickHere+src%3D%22.%2Fbutton.png%22%3E

なんと、img タグのコード文字列を丸々サーバに送ってしまうのだ。これは酷い実装だ!せめて alt 属性の中身でも送ってくれれば、まだ救いようがあっただろうに。

複数のボタン

IE には、もう一つ重要な button 要素に関するバグがある。それは、submit 型のボタンを複数用意した場合の動作である。具体的な例をまず提示しよう。

<form action="index.cgi">
  <p>
    <button type="submit" name="command" value="yes">Yes</button>
    <button type="submit" name="command" value="no">No</button>
  </p>
</form>

同じ名前が割り当てられた、2つの提出ボタンがある。このうち Yes と書かれた一つ目のボタンだけを押してみよう。

GekkoやOperaの場合
command=yes
Internet Explorerの場合
command=Yes&command=No

button 要素のどの部分が送られているかはこの際無視しよう。Gekko や Opera では、押したほうのボタンの値だけが送られている。 では IE はどうだろう。驚くべきことに、全く押していないボタンを含む、両方の値が送られている。これは "No" と書かれたボタンを押しても全く変わらない。つまりは送られてきた文字列を見る限りでは、どのボタンが押されたかを全く判別することが出来ないのだ。

確認のために、HTML 仕様書(邦訳)を紐解いてみよう。満足なコントロールという項目の中に、その規定が書かれている。

  • 1つのフォームに複数の提出ボタンがある場合、アクティブにされた提出ボタンのみが満足となる。

Web サーバに提出されるべき値は、この満足なコントロールの条件を満たしたものでなければならない。押されていないほうのボタンは、満足なコントロールではない。 つまりは IE の実装は間違いであるということだ。

button 要素が素晴らしいワケ

button 要素は凄い。何が凄いって、送信する文字列と表示する文字列を別々に出来るからスゴいのだ。 ユーザにはわかりやすい日本語のラベルを見せておいて、サーバには扱いやすい ASCII な値を送ってやる。そんな当たり前ことが、input 要素には出来ない。でも button 要素にはできる。そう、IE さえ対応していれば!

属性セレクタに対応していない CSS1 ブラウザは、input 要素で作られた押しボタンも入力フィールドも見分けることが出来ない。でも button 要素ならタイプセレクタで見分けられるから、ボタンに独自のスタイルを定義することが出来る。これを使わなければ、全部の押しボタンにクラス名を割り振るしかない。

ボタンの中身に画像とテキストを同時に表示することもできる。これも input 要素には出来ない芸当だ。

DOM を使った解決策

button は全く使いものにならない。この事実は IE6.0 が世界のシェアから90%以上消滅するまできっと続くだろう。残念ながらあきらめるしかない。

それならばせめて、DOM でそれらしいものを作ってみよう。DOM プログラミングの醍醐味は、なんといっても後方互換性をどうやって用意するかだ。ある真っ当なHTML断片を、全く違う形にして表現する。でも機能は決して変わらない。そんなポリシーを抱きつつ、擬似 button 要素を DOM で作ってみよう。条件は以下の二つだ。

  • 表示するラベルと送信する値を異なるものにする
  • JavaScript 無しでも同等のフォーム内容を送信できる
<form action="index.cgi">
  <p>
    <input type="radio" name="command" id="yes" value="yes" />
    <label for="yes">はい</label>
    <input type="radio" name="command" id="yes" value="no" />
    <label for="no">いいえ</label>
    <button type="submit">送信</button>
  </p>
</form>

このラジオボタンとラベルの組み合わせを、DOMとCSSの組み合わせによって無理やりボタンに見せかける。元の送信ボタンはCSSで隠してしまい、変わりにラベルをクリックしたときにそのまま送信してしまう。それを実現するためのスクリプトとスタイルシートは次のようなものだ。

ボタンの実験ページから確認してみることもできる。 JavaScriptのオン、オフを簡単に切り替えられる環境があるなら、DOM がどのように表示を制御しているか見ることができるだろう。

このテクニックを使えばある程度は実現できるが、やぱり限界はある。たとえば古いバージョンのOperaを使っている場合、ブラウザがクラッシュしてしまう事がある。 DOMとはいえ、やあり小手先のテクニックに過ぎないのだろう。 keypresstabindex を使って、最低限のアクセシビリティは確保しているが、やはりボタン“もどき”であり、あらゆるインタフェースに対応できるわけではない。

button はデータの送信という、ユーザタスクの中で最もコアな位置を占めている要素の一つなので、なんとしてもまともな実装をしてもらいたいものだ。

投稿者 : 09:01 | トラックバック (0)

トラックバック

このエントリーのトラックバックURL:
http://totora.jpn.org/mt/mt-tb.cgi/47