【HTML】dl, dt, ddで組みたくなる表、tableにするのがいいかもね(スクリーンリーダーと検索エンジンのために)
はじめに結論から
こういう dl
で組みたくなる表(キーと値が対応付けられた、メタデータを示す表)は、table
で組むことをおすすめします!
理由
- スクリーンリーダーで読み上げたときに意図した項目数・役割が読み上げられる(アクセシビリティ上のメリット)
- 名前と値が 1 対 1 対応であるという情報構造が機械可読である(SEO 上のメリット)
- 必要に応じて、追加の列や、見出し行を追加できる(拡張性)
比較表
dl (dt , dd ) |
ul (li , hn , p ) |
table (tr , th , td ) |
|
---|---|---|---|
項目数の読み上げ | 🔺 | ✅ | ✅ |
見出しと値の区別 | 🔺 | ✅ | ✅ |
名前-値の構造の機械可読性 (≒SEO上のメリット) |
❌ | ❌️ | ✅ |
見出し行 | ❌ | ❌ | ✅ |
追加の列 | ❌ | ❌ | ✅ |
🔺…スクリーンリーダーによる
詳しくは以降で説明します。
想定する表の内容
この記事の議論では、名前と値の組が複数並んでいる、メタデータの表を想定します。
プログラミング言語でいうところの、連想配列 (Map, Dictionary, JS では Object) の構造に相当します。
具体的には以下のようなものです。
- 会社概要(「会社名:〇〇、所在地:〇〇、資本金:〇〇、…」)
- 商品の仕様表(「商品名:〇〇、価格:〇〇、サイズ:〇〇、…」)
- 人やキャラクターのプロフィール(「名前:〇〇、年齢:〇〇、出身地:〇〇、…」)
この記事中では、ポケモン(カビゴン)の図鑑情報を例に取り上げます。
比較する手段
📢 今回出場する選手たちは以下の 3 チーム!
-
dl
(div
,dt
,dd
) -
ul
(li
,hn
,p
) -
table
(tr
,th
,td
)
先にそれぞれの実装例を紹介して、その後比較していきます。
なお、スタイリング(CSS)に関してはいずれの方法でも問題がないと判断して、特に比較は行っていません。
dl
エントリーNo.1 dl
要素を使用する場合、次のような HTML 構造で作成します。
<dl>
<div>
<dt>図鑑ナンバー</dt>
<dd>0143</dd>
</div>
<div>
<dt>タイプ</dt>
<dd>ノーマル</dd>
</div>
<div>
<dt>説明文</dt>
<dd>1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。</dd>
</div>
</dl>
- 全体を
dl
要素で囲む - スタイリングのため、各行を
div
要素で囲む -
dt
要素で、見出しを示す -
dd
要素で、見出しに対応する値を示す
使い方が適切かどうか、 dl要素のHTML仕様(日本語訳)を参照しておきます。
Name-value groups may be terms and definitions, metadata topics and values, questions and answers, or any other groups of name-value data.
名前-値グループは、用語とその定義、メタデータの見出しや値、質問と回答、または名前-値のデータのグループであってもよい。
とあり、この用途は「メタデータの見出しや値」にあたりそうなので、妥当でしょう。
ul
エントリーNo.2 ul
を使用する場合、次のような構造を想定します。
<ul>
<li>
<h3>図鑑ナンバー</h3>
<p>0143</p>
</li>
<li>
<h3>タイプ</h3>
<p>ノーマル</p>
</li>
<li>
<h3>説明文</h3>
<p>1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。</p>
</li>
</ul>
- 全体を
ul
要素で囲む - 各行を
li
要素で囲む -
h3
要素で、見出しを示す(見出しレベルは状況に応じて) -
p
要素で、見出しに対応する値を示す
table
エントリーNo.3 次のような構造を想定します。
<table>
<caption>カビゴンの情報</caption>
<tbody>
<tr>
<th>図鑑ナンバー</th>
<td>0143</td>
</tr>
<tr>
<th>タイプ</th>
<td>ノーマル</td>
</tr>
<tr>
<th>説明文</th>
<td>1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。</td>
</tr>
</tbody>
</table>
- 全体を
table
要素で囲む -
caption
要素で、テーブルの内容を説明するテキストを指定する- WAI-ARIA の仕様上、
table
ロールはアクセシブルな名前が必須とされているため記載。HTML 仕様においては、なくても問題はありません
- WAI-ARIA の仕様上、
-
tbody
要素で、テーブルの本体を示す -
tr
要素で、行を示す -
th
要素で、見出しを示す-
scope="row"
属性で、見出しの範囲を明示することも可能。この構造の場合はscope
属性を省略しても行ヘッダーとして認識されるため省略
-
-
td
要素で、見出しに対応する値を示す
第1試合 - スクリーンリーダーで読み上げ
以前読み上げの比較をした CodePen がありますので、参考に載せておきます。
dl
は意図した読み上げにならない
dl で組んだ表をスクリーンリーダーで読み上げてみると、とある問題が判明します。
以下に NVDA で読み上げた結果を示します。
リスト 6項目
図鑑ナンバー
0143
タイプ
ノーマル
説明文
1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。
リストの外
注目するポイントとしては以下の通りです。
- ✅「リスト」と読み上げられ、並列の項目が並んでいることがわかる
- ❌(3 項目のはずなのに)6項目と読み上げられてしまう
- ❌ 見出しと値が区別されない
本来、「図鑑ナンバー」「タイプ」「説明文」という 3 項目があることを伝えたいのですが、6 項目と読み上げられてしまいます。
これは、dt
の数ではなく dt
とdd
の合計を読み上げてしまうことによります。
また、見出しと値を区別する読み上げがされていないので、今読んでいるのが見出しなのか値なのかわかりません(「図鑑ナンバー」「タイプ」「説明文」という内容からなんとなくわかるとはいえ)。
MDN に以下の説明がとある通り、dl
, dt
, dd
の読み上げ方法は定まっておらず、意図した読み上げになりません。
スクリーンリーダーによって、
<dl>
コンテンツの合計数、用語/定義のコンテキスト、ナビゲーション方法の公開方法は様々です。これらの違いは、必ずしもバグではありません。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/dl#アクセシビリティの考慮
改善の見込みは…
本当は WAI-ARIA に associationlist
という role が実装され、読み上げが改善されそうな見込みがありました(過去形)。
というのも、一時はこの仕様が WAI-ARIA 1.3(草案)にあったのですが、現在は削除されたようです。
経緯まで把握できていないのでなんとも言えませんが、改善されるとしてもかなり先のことになりそうです…。
ul
は正しい項目数が読み上げられる
ul
で作成した例を読み上げると以下のような結果になります。
リスト 3項目
見出し レベル3 図鑑ナンバー
0143
見出し レベル3 タイプ
ノーマル
見出し レベル3 説明文
1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。
リストの外
- ✅「リスト」と読み上げられ、並列の項目が並んでいることがわかる
- ✅ 3 項目と読み上げられる
- ✅「見出し」と読み上げられるので、見出しと値が区別される
ul
は、 li
の数を読み上げるため、「3 項目」と正しく読み上げられます。
また、見出しとしてマークアップしたことで、各項目の役割がわかりやすくなります。
table
は正しい項目数(行数)が読み上げられる
table
で作成した例を読み上げると以下のような結果になります。
テーブル 3行2列のテーブル カビゴンの情報
1行 1列 図鑑ナンバー
2列 0143
2行 1列 タイプ
2列 ノーマル
3行 1列 説明文
2列 1にちに たべものを 400キロ たべないと きが すまない。たべおわると ねむってしまう。
テーブルの外
- ✅「テーブル」と読み上げられ、構造を持った表であることがわかる
- ✅ 3 行と読み上げられる
- ✅ 列数によって、見出しと値が区別できる
📢 というわけで、第 1 試合スクリーンリーダーでは、ul
と table
に軍配が上がりました🏆
第2試合 - 構造の機械可読性(≒SEO上のメリット)
ul
と table
の違いを見ていきます。
今回のケースでは、「図鑑ナンバー=0143」「タイプ=ノーマル」のように、名前と値が 1 対 1 で対応しているため、この構造が伝わるとベストです。
ul
の中で利用した hn
と p
はあくまで階層構造
ul
の実装例では、見出しに h3
要素、値に p
要素を使って構造を表しました。
しかし、h3
要素はあくまで階層構造を示すものであり、見出しと値の 1 対 1 関係を示すものではありません。
今回の実装例では p
を 1 つしか使わないことでデータを表しましたが、本来は p
が複数出現してもいい、ということをイメージすると、このことがわかりやすいと思います。
table
は見出しと値の関係を機械的に認識できる
テーブルでは「列」という概念がありますから、見出しと値の関係を機械的に認識できます。
これは単なる思想の話ではなく、実用上のメリットが生まれることがあります。
それは検索エンジンで読み取られる際のことです。
例えば、table
で実装されているページの例として、大塚製薬の会社概要を Bing で検索してみると、メタデータとして解釈されて検索ページ上に表示されていました。
また、(今回議論の対象としている表とは異なりますが、)より複雑な表の場合はよりリッチな表示(強調スニペット)として表示されることがあります。(例:HTMLタグ/HTML要素一覧 - TAG index)
上記は Bing での例を紹介しましたが、以前までは Google 検索でも同様に強調スニペットとして、テーブルの内容が表示されることがありました。
しかし、記事執筆時点の 2024 年 4 月 6 日現在は確認できなくなっていました。この変更が一時的なものなのか恒久的なものなのかはわかりません(もし詳細知っている方いればコメントで教えてください)。
そのため効果は低減してしまっているのですが、Bing では現役なので考慮しておく価値はあると思います。
参考までに、以前の Google がテーブルを強調スニペットとして表示していた様子を載せている記事を以下に紹介します。
📢 というわけで、第 2 試合構造の機械可読性では、table
が勝利しました🏆
エキシビションマッチ - 拡張性
もしも、それぞれの列のラベルをつけるために見出し行を追加したくなったり、さらに情報を説明するために列を追加したくなった場合を考えます。
(想定している表とは異なる仮定を追加してしまうので、エキシビションマッチとしてます。)
dl
と ul
は見出し行や追加の列を追加できない
dl
と ul
(hn
とp
)には、見出し行のような構造を作ることはできません。
追加の列に関しては、それぞれ追加の dd
、p
を追加することはできますが、機械可読性の項目でも述べた通り、列としての構造にはなりません。
table
は見出し行や追加の列を追加できる
table
要素は、thead
, tfoot
要素を使って、見出し行や集計行を追加できます。
そのため、「項目」「内容」のような見出しを必要に応じて追加できます。
<table>
<caption>カビゴンの情報</caption>
+ <thead>
+ <tr>
+ <th>項目</th>
+ <th>内容</th>
+ </tr>
+ </thead>
<tbody>
<tr>
<th>図鑑ナンバー</th>
<td>0143</td>
</tr>
<tr>
<!-- 略 -->
</tr>
<tr>
<!-- 略 -->
</tr>
</tbody>
</table>
さらに列を追加することもできます。
<table>
<caption>カビゴンの情報</caption>
<thead>
<tr>
<th>項目</th>
<th>内容</th>
+ <th>備考</th>
</tr>
</thead>
<tbody>
<tr>
<th>図鑑ナンバー</th>
<td>0143</td>
+ <td>ポケモンの数が1000を超えたため、4桁で表記されるようになりました</td>
</tr>
<tr>
<!-- 略 -->
</tr>
<tr>
<!-- 略 -->
</tr>
</tbody>
</table>
📢 拡張性の観点でも、table
にはメリットがありました🥇
使用事例を見てみる
実際に使用されている例を見てみます。ある程度マークアップが信頼できるサイトから、今回の対象である名前と値が 1 対 1 で対応しているメタデータを示す表を探しました。
今回確認した 3 例はどれも table
で実装されていました。
MDN
MDN の HTML 要素のページにある、その要素の基本情報を示す「技術的概要」の表は table
でした。
W3C
WAI-ARIA の仕様において、role の情報を説明する "Characteristics" の表は table
でした。
また、この例では "Characteristics", "Value" という見出し行があります。
Wikipedia
Wikipedia の基本情報の表は table
でした。
まとめ
名前と値が 1 対 1 で対応している情報を示す表について、dl
ul
table
のそれぞれの実装方法を比較しました。
- スクリーンリーダーでの読み上げ
- 情報構造の機械可読性(SEO 上のメリット)
から table
が優位 であるという結論に至りました。
実装方法に迷った際には参考にしてください。
Discussion