はじめに
この記事は 2023 年の MDN 翻訳 Advent Calendar 向けに作成したものです。
こんにちは。debiru です。XHTML の X は Extensible の Ex の音を表しているので eXtensible と表記すべきではない委員会から来ました。
さて、今日も今日とてネタが見当たらないので HTML のお話をいくつか紹介してみます。
HTML のお話 5 選
思いついた HTML のお話を 5 個くらい書いてみます。
1. a
要素と button
要素を使い分けよう
ユーザーがクリックして操作するものを div
や span
でマークアップしてはいけません。と言われて久しいものですが、マークアップしてはいけない理由は分かるでしょうか。
例えば div
をボタン風に仕立てるためにクリックイベントを設定したとしても、マウスを用いずにキーボード操作しているユーザーがいた場合、ユーザーは div
要素を「クリック」できなくなってしまいます。なぜならキーボード操作によるフォーカスの対象にはならないからです。では、フォーカスの対象にするような JavaScript を書けばいいのでしょうか。もしかしたら実装が不十分で、また違う環境のユーザーが操作できない可能性が残ってしまうかもしれません。
HTML には専用のインタラクティブ要素が存在しています。クリック操作が想定されているものとして a
要素や button
要素があります。これらはデフォルトでキーボード操作によるフォーカスの対象となるので、クリックイベントを設定するだけで、マウスを用いないユーザーでも問題なく操作できることが期待できます。
では、クリックによる操作を実現したい場合は button
要素を使っておけば良いのでしょうか?答えは No です。button
要素には実現できない機能が存在しています。それはユーザビリティのあるリンク機能の提供です。
リンク機能の提供をする場合は button
要素ではなく a
要素を使いましょう。なぜなら、a
要素には、未訪問か訪問済みかの状態を表示しわけることができたり、Control
や Command
キーを押しながらクリックする、あるいは「右クリック」でコンテキストメニューを表示することによって別タブでページを表示することを提供する機能があるためです。
Wiki システムから脱却し GitHub ベースで管理がされるようになった現在のデザインの MDN ページでは、ページの右上に言語切替メニューが存在しています。この言語切替ボタンがこれまで button
要素で実装されていたために、ユーザーは別タブで別言語のページを開くことができませんでした(button
要素でも別タブで開くことができる一部のブラウザを除いて)。
これはユーザビリティの低下を招いてしまっています。この問題については 2023 年 4 月に私が修正を提案し、8 月に a
要素での実装となるよう修正が行われました。
みなさんも、他の処理を伴わない単なるリンクが button
要素で実装されているものを見つけたら、a
要素で実装すべきことを提案しましょう。
2. <button>
と <input type="button">
の違い、type
属性のデフォルト値について
input
要素の歴史は古く、HTML 2.0 時代には存在していました。この頃から基本的なフォーム機能が提供されており、Web ページ制作者はフォーム機能を利用することができました。
button
要素は HTML 4.0 で登場しました。input
要素によるボタン(type
属性値が button
, reset
, image
などのもの)との違いは、button
要素が内容を持てる(空要素ではない)という点にあります。button
要素は input
要素によるボタンの拡張要素なのです。つまり、button
要素の方が高機能なのです。button
要素であれば、中身に「画像とテキスト」を同時に入れたり、画像に代替テキストを設定することができます。
そんな便利な button
要素ですが、歴史的な理由により、type
属性を省略した際のデフォルト値が submit
になっています。これは、form
要素内に button
要素を設置した際に、type
属性を指定しない場合、ボタンのクリック操作によってフォームがサブミットされてしまうことを意味します。
JavaScript から操作する単純なボタンとして利用したい場合は type="button"
を指定する必要があります。また、そのような混乱を避けるために、button
要素を利用する場合は、submit
として利用する場合であっても、常に type
属性を明示的に指定するようにした方がよいでしょう。
みなさんも、type
属性値が指定されていない button
要素を見つけたら、type
属性を指定すべきことを提案しましょう。
3. <br>
と <br />
はどう違う? - ポリグロット・マークアップの幻想
みなさんご存知の通り HTML (Hyper-Text Markup Language) はマークアップ言語ですが、HTML にはベースとなった SGML (Standard Generalized Markup Language) というマークアップ言語が存在しています。初期の HTML は SGML のサブセットとして定義され、HTML 4.01 までは SGML のサブセットとして HTML が構成されていました。
SGML には GML (Generalized Markup Language) という更にベースとなった言語が存在しています。SGML の歴史については SGML誕生のいきさつ - dsssl.info が詳しいです。
SGML には省略タグ機構や短縮タグ機構というものが存在しています。省略タグ機構の規則と HTML の DTD (Document Type Definition) によって、HTML 4.01 では一部の要素の開始タグや終了タグを省略することができていました。また、SGML では空要素(Empty Element, HTML5 では Void Element と改名された)は終了タグを省略しなければならないと定められていました。
SGML はタグの省略ができるなど自由度が高い反面、どの要素が空要素なのか判定できないと要素内容を正しく解析することが不可能でした。こうした問題を回避し、任意の用途向けに拡張することを容易にしたマークアップ言語として SGML のサブセットという形で XML (Extensible Markup Language) が登場しました。XML では全ての要素タグが省略できません。
HTML を XML ベースで定義しようという動きがあり、登場したのが XHTML 1.0 です。XHTML では XML に準拠した HTML を書くことになります。
ここで注目すべきなのが、空要素タグの書き方です。
- HTML 4.01 - 終了タグを省略して
<br>
と書くことになっている。 - XHTML 1.0 - 終了タグを省略せずに
<br />
のように書く。
あれ?終了タグを書くということは <br></br>
になるのではないでしょうか。しかしこれは旧来の SGML にあった「空要素は終了タグを省略しなければならない」に反します。
SGML には短縮タグ機構というものがあり、その一部に簡略終了タグ(NET; Null End Tag)と、さらに NET 可能開始タグ終了区切り子(NESTC; NET-Enabling Start-Tag Closer) と呼ばれるものがあります。NET の解説は SGML の短縮タグ機構 | 鳩丸よもやま話が詳しいのでそちらをお読みください。
結局、<br></br>
は NET および NESTC により <br/>
と書けるのでした。この短い形式の /
が開始タグの括弧閉じで、>
が終了タグそのものを表しているのです。しかし、<br/>
のように要素名と /
を繋げて書くと、br/
を要素名だと解釈してしまう User Agent が当時は存在していました。そのような User Agent が要素名を勘違いしないようにスペースを付与して <br />
のように空要素タグを記述するようになったのです。
これが <br>
と <br />
の違いというか表記が分かれた経緯でした。HTML 4.01 では前者を、XHTML では後者を記述することになるわけです。
では、HTML5 ではどちらを記述するとよいのでしょうか。なお、もはや HTML5 は SGML ベースでも XML ベースでもありません。
答えは <br>
です。なぜなら、XML に準拠した HTML5(XHTML5 とも呼ばれる)は幻想だからです。XHTML5 は Polyglot Markup(ポリグロット・マークアップ)とも呼ばれます。幻想とはいったい何のことでしょうか。私にも分かりませんが、幻想ということらしいです。それでもなお、XHTML5 を目指している人は <br />
のように記述すればよいでしょう。記述して悪い道理は一つもありません。
Markup Validation Service - W3C では、空要素タグを <br />
のように書くともはや "Trailing slash on void elements has no effect and interacts badly with unquoted attribute values." と言ってきます。
みなさんも、空要素タグを <br />
のように閉じているマークアップを見かけたら、<br>
のように統一して書くことを提案しましょう。
4. HTML5 における dl
要素の進化と障壁
dl
要素の dl
は何の略でしょうか。そう、Definition List(定義リスト)です。このような定義が身に付いた読者はどれほどいるでしょうか。いると嬉しいのですが。いや、しかし、ご存知の通り正しくは Description List(説明リスト)です。HTML 4.01 / XHTML までは前者でしたが、HTML5 で改定されたのです。
そして HTML5 では、di
要素(Description Item)こそ互換性の問題で採用されなかったものの、同等のグルーピングを可能にする dl
直下の div
が採用されました。これは次のようなマークアップを可能にします。
<dl>
<div class="di">
<dt>Name</dt>
<dd>Godzilla</dd>
</div>
<div class="di">
<dt>Born</dt>
<dd>1952</dd>
</div>
<div class="di">
<dt>Birthplace</dt>
<dd>Japan</dd>
</div>
<div class="di">
<dt>Color</dt>
<dd>Green</dd>
</div>
</dl>
class="di"
は必須ではありませんが、私はこの特殊な div
に対しては class="di"
を与えるようにしています。di
要素は XHTML 2.0 で提案された要素です。
このような div
を伴うマークアップの dl
要素は文書構造として強力です。なぜなら以下のような tbody
が存在しない table
要素と同等の構造を持つからです。display
プロパティを駆使すれば、dl
要素のマークアップで表組レイアウトが実現できます。これは HTML 4.01 時代の dl
要素にはできなかったことです。
<table>
<tr>
<th>Name</th>
<td>Godzilla</td>
</tr>
<tr>
<th>Born</th>
<td>1952</td>
</tr>
<tr>
<th>Birthplace</th>
<td>Japan</td>
</tr>
<tr>
<th>Color</th>
<td>Green</td>
</tr>
</table>
しかし、HTML5 時代の dl
要素には障壁もあります。
HTML5 に起因した問題ではないですが、dl
要素はアクセシビリティツリー上の表現に問題があるのです。なんと、上記の dl
要素のマークアップを、「4 つの項目と説明」ではなく「8 つのリスト」と読み上げるのです。詳しくは dl/dt/ddのスクリーンリーダーの読み上げをなんとかする - ゆうてんを参照してください。
とはいってもこれはスクリーンリーダー側の問題として解決を望んだほうがよいでしょう。これを理由に dl
要素を使わないという選択をする人もいるようですが、せっかく dl
要素で表現できる幅が広がったのですから活用していきたいものです。
みなさんも、div
を使わずに dl
要素タグでマークアップしている例を見つけたら、div
でグルーピングすると幸せになれるよと提案しましょう。
5. fieldset
要素を活用しよう
フォームのマークアップにおいて、次のような例を考えましょう。
...
<h2>注文</h2>
<form action="..." method="post">
<h3>注文者情報</h3>
<ul>
<li><label>氏名<input type="text" name="customer-name"></label></li>
<li><label>郵便番号<input type="text" name="customer-zipcode"></label></li>
<li><label>住所<input type="text" name="customer-address"></label></li>
</ul>
<h3>お届け先情報</h3>
<ul>
<li><label>氏名<input type="text" name="shipping-name"></label></li>
<li><label>郵便番号<input type="text" name="shipping-zipcode"></label></li>
<li><label>住所<input type="text" name="shipping-address"></label></li>
</ul>
...
</form>
上記は一見、悪くなさそうなマークアップに見えますが、致命的な問題があります。スクリーンリーダーの利用者がキーボード操作しているような状況では、input
要素にフォーカスして、それに関連付けられたラベル文言が読まれますが、例えば 1 つ目の input
要素にフォーカスすると「氏名のテキスト入力」と読まれます。これでは、何に対する氏名なのか分かりません。
これを解決するのが fieldset
要素です。fieldset
要素を用いて次のようなマークアップをしてみましょう。
...
<h2>注文</h2>
<form action="..." method="post">
<fieldset>
<legend>注文者情報</legend>
<ul>
<li><label>氏名<input type="text" name="customer-name"></label></li>
<li><label>郵便番号<input type="text" name="customer-zipcode"></label></li>
<li><label>住所<input type="text" name="customer-address"></label></li>
</ul>
</fielset>
<fieldset>
<legend>お届け先情報</legend>
<ul>
<li><label>氏名<input type="text" name="shipping-name"></label></li>
<li><label>郵便番号<input type="text" name="shipping-zipcode"></label></li>
<li><label>住所<input type="text" name="shipping-address"></label></li>
</ul>
</fieldset>
...
</form>
legend
要素は fieldset
要素の最初の子でなければなりません。
このようなマークアップをしていると、スクリーンリーダーでは 1 つ目の input
要素にフォーカスした際に「注文者情報 氏名のテキスト入力」のように読み上げられることが期待できます。fieldset
の legend
要素は、input
要素などのフォームコントロールをグループ化すると同時に、フォームコントロールに対してそれが何に属する情報なのかを関連付けることができます。
fieldset
と legend
要素は地味ですがアクセシビリティを向上する上で非常に強力な道具になります。ぜひ積極的に利用しましょう。
みなさんも、fieldset
を使わずに form
要素タグでマークアップしている例を見つけたら、fielset
でグルーピングすると幸せになれるよと提案しましょう。
さいごに
ネタが思いつかなくて HTML 関連のトピックを A から Z まで順に思い付くだけ書こうかと思ったのですが、5 個書いただけでもこの分量になってしまったので私の構想は F で打ち止めとなりました。
PHP の var_dump()
とかを直接出力するなら xmp
要素が便利だよとか書こうと思ったのですが、xmp
要素を知っていたという方はいますか。この記事を読んでいる HTML マニアな方なら知っているかもしれませんね。
それではまたお会いしましょう。
おわり。