表は、見出しと値を組み合わせることで、必要な情報だけを選択して取得することができるとても便利なフォーマットです。しかし、見出しと値を同時に参照できることで初めてその有用性を発揮します。視覚機能に問題のない方であれば、見出しと値セルを同時に見ることも可能ですが、視覚障害者等の音声ユーザーは、スクリーンリーダーでフォーカスがあったところの情報を順番に読み上げて取得するため、表を読み上げる場合、見出しから離れた箇所で例えば、値セル「300円」と読み上げられても、何が300円かわかりませんので、自分が求める情報を特定することができません。「アイスコーヒー 300円」のように、値セルにある「300円」という情報と関連する見出しの「アイスコーヒー」が同時に読み上げられる機能が利用できることが重要です。
「table要素内の th 要素や scope 属性の有無によるスクリーンリーダーの読み上げの違い」(以下、「前エントリ」)というエントリを3月に書いたことがあり、テーブルの見出し読み上げについて、一度検証したことがありました。その際にheaders属性の検証をしていなかったことと、改めて検証してみると誤りもあったこと(すいません)、3月時点では対応していなかった(と思われた)PC-Talkerの見出し対応が確認されたことから、改めて確認しました(誤りがあったこと、また、すでに情報が古くなっていることから、このエントリを公開のタイミングで前エントリは非公開とました)。
本エントリは、kzakzaが確認できる範囲でまとめた検証結果及び結論ですでの、誤りがある可能性があります。その点をご留意ください。
1. 検証対象
今回検証対象とした環境は以下のとおりです。可能な限り最新のものを検証するようにしましたが、JAWSの検証環境については、最新のJAWS 2018より2つ前のバージョンで検証していますので、ご留意ください。
- PC-Talker 10 ver.1.25(体験版) + Internet Explore 11[Windows 10 ver.1803](以下、表では「PT10 + IE11」)
- PC-Talker 10 ver.1.25(体験版) + NetReader 2 ver.2.31(体験版) [Windows 10 ver.1803](以下、表では「PT10 + NR2」)
- NVDA 2018.2.1jp+ Firefox 62.0[OS: Windows 10 ver.1803](以下、表では「NVDA + FF」)
- JAWS 日本語版 17.0.2729 + Internet Explore 11, Chrome ver.61.0.3163.100 [OS:Windows7](以下、表では「JAWS17 + IE11, FF, Chrome」)
- ナレーター + Edge [Windows 10 ver.1803](以下、表では「ナレーター + Edge」)
- VoiceOver(iOS版) + Safari[OS:iOS 11.4.1](以下、表では「VO (iOS) + Safari」)
- VoiceOver(Mac版) + Safari[OS: macOS High Sierra 10.13.6](以下、表では「VO (Mac) + Safari」)
なお、PC-Talker 10と同じ挙動すると思われたことから、前エントリで検証したPC-Talker7(Windows7版のPC-Talker7)の検証を省略しました(PC-Talker10ほどがりがりに検証してはいませんが、PC-Talker7も見出し読み上げに対応していることは一応は確認済み)。
2. 各環境のテーブルまわりの基本操作と機能
備忘的に各種環境のテーブルまわりの機能と操作方法を以下の通りまとめました。
3. 先に検証結果をまとめておく
本エントリでの検証は、 後述の4.個別検証結果に掲載したパターン1からパターン6の検証用のサンプルテーブルを1. 検証対象であげたそれぞれの環境で読み上げてみることで、確認しました。詳細は4. 個別検証結果のとおり、まとめていますが、これらの検証結果をうけてとりあえず先にまとめをしておきます。
各要素・属性の利用の要否について
- caption要素
- table要素の直後に配置したcaption要素のテキストにはテーブルにフォーカスがあったタイミングで一番最初にフォーカスが当たる。Tキーなどでテーブル単位で移動する場合もキャプションからどの表であるかがわかるので、キャプションがある表にはそのキャプションにcatpion要素を必ず用いるべき。
- th 要素
- いずれの環境でもth 要素による見出し指定は対応している。th要素のみで複雑な表の構成を解析して、期待通りの読み上げをしてくれる環境もあった。見出しがある表ではth要素は必ず用いるべき。
- scope 属性
- scope属性の対応の有無は環境によって分かれたが、scope属性で見出しのスコープを指定することで、対応する環境で期待どおりの読み上げが行われるメリットはあっても、対応しない環境で期待されない挙動を招くようなデメリットはないので、th要素とともにscope属性は指定しておくことが望ましい。
- headers 属性
- headers は現時点で完全に対応していると思われる環境はPC-Talker10 + NetReader2の環境のみであった。また、一部でもheaders属性に対応している場合、scope属性よりheaders属性が優先されることが確認できた。そのため、th要素とscope属性 のみであれば、期待通りの見出しを読み上げた表が、headers 属性で見出しを指定されることで、headers 属性への不完全な対応にもあいまって、逆にheaders 属性がなければ読まれる見出しがそれがあることで読まれなくなる事例もあった。現時点では、headers 属性を使用することは避けたほうがよいと思われる。
各環境における各要素、属性の対応状況
以下はそれぞれの環境における各要素、属性の対応をまとめた表です。
※headers 属性の「一部対応」の詳細は、パターン6(caption 要素あり/ th 要素あり / scope 属性あり/ 見出しが複数列・行 / 結合あり/ headers 要素あり) の検証結果を参照。
複雑な表への対応方法(値セルに対応する見出しの読み上げ)
パターン4やパターン6のサンプルあるような見出しが複数行・列に渡ったり、結合される表への対応方法についても以下のとおりまとめました。
4. 個別検証結果
パターン1(caption 要素あり/th等すべてなし)
検証目的
検証目的: caption 要素の対応の確認及び th 要素等の見出し情報なしでの読み上げ状況
期待される挙動: 値セルテーブルにフォーカスがあったタイミングでcaption 要素のテキストにフォーカスがあたって読み上げる。
検証サンプル
<table>
<caption style="caption-side: bottom;">パターン1(caption 要素あり/ th 要素等すべてなし) </caption>
<thead>
<tr><td>列見出し1</td><td>列見出し2</td><td>列見出し3</td><td>列見出し4</td><td>列見出し5</td></tr>
</thead>
<tbody>
<tr><td>行見出し1</td><td>値1</td><td>値2</td><td>値3</td><td>値4</td></tr>
<tr><td>行見出し2</td><td>値5</td><td>値6</td><td>値7</td><td>値8</td></tr>
</tbody>
</table>
検証結果
パターン2(caption 要素あり/ th 要素あり/ scope 属性なし)
検証目的
検証目的: th 要素の対応及び対応状況の確認
期待される挙動: 値セルにフォーカスがあったタイミングで適切な th 要素の見出しを読み上げる。
検証サンプル
<table>
<caption style="caption-side: bottom;">パターン2(caption 要素あり/ th 要素あり/ scope 属性なし) </caption>ption>
<thead>
<tr><th>列見出し1</th><th>列見出し2</th><th>列見出し3</th><th>列見出し4</th><th>列見出し5</th></tr>
</thead>
<tbody>
<tr><th>行見出し1</th><td>値1</td><td>値2</td><td>値3</td><td>値4</td></tr>
<tr><th>行見出し2</th><td>値5</td><td>値6</td><td>値7</td><td>値8</td></tr>
<tr><th>行見出し3</th><td>値9</td><td>値12</td><td>値11</td><td>値12</td></tr>
</tbody>
</table>
検証結果
パターン3(caption 要素あり/ th 要素あり / scope 属性あり)
検証目的
検証目的: scope属性の対応及び対応状況の確認。パターン2との挙動の違いの確認。
期待される挙動: 値セルにフォーカスがあったタイミングでscope属性で指定された見出しを読み上げる。scope属性で関連付けられていない値セルで当該見出しを読み上げない。
検証サンプル
<!-- 「列見出し1」を列見出しとしてscope属性"col"で指定-->
<table>
<caption style="caption-side: bottom;">パターン3-1<scope属性で列見出し指定>(caption 要素あり/ th 要素あり / scope 属性あり)</caption>
<tbody>
<tr><th scope="col">列見出し1</td><td>値1</td><td>値2</td><td>値3</td></tr>
<tr><td>値4</td><td>値5</td><td>値6</td><td>値7</td></tr>
<tr><td>値8</td><td>値9</td><td>値10</td><td>値11</td></tr>
</tbody>
</table>
<!-- 「行見出し1」を行見出しとしてscope属性"row"で指定-->
<table>
<caption style="caption-side: bottom;">パターン3-2 <scope属性で行見出し指定>(caption 要素あり/ th 要素あり / scope 属性あり)</caption>
<tbody>
<tr><th scope="row">行見出し1</td><td>値1</td><td>値2</td><td>値3</td></tr>
<tr><td>値4</td><td>値5</td><td>値6</td><td>値7</td></tr>
<tr><td>値8</td><td>値9</td><td>値10</td><td>値11</td></tr>
</tbody>
</table>
検証結果
パターン4(caption 要素あり/ th 要素あり/ scope 属性なし/ 見出しが複数列・行 / 結合あり )
検証目的
検証目的: 複数列・行の見出しの場合の th 要素の対応及び対応状況の確認( th 要素のみで複数列・行の見出しを読み上げるか)。
期待される挙動: 値セルにフォーカスがあったタイミングで適切な複数の th 要素の見出しを全て読み上げる。
検証サンプル
<table>
<caption style="caption-side: bottom;">パターン4(caption 要素あり/ th 要素あり/ scope 属性なし/ 見出しが複数列・行 / 結合あり )</caption>
<thead>
<tr><th rowspan="2" colspan="2">列見出し1</th><th>列見出し2</th><th>列見出し3</th><th colspan="2">列見出し4</th></tr>
<tr><th>列見出し5</th><th>列見出し6</th><th>列見出し7</th><th>列見出し8</th></tr>
</thead>
<tbody>
<tr><th rowspan="2">行見出し1</th><th>行見出し4</th><td>値1</td><td>値2</td><td>値3</td><td>値4</td></tr>
<tr><th>行見出し5</th><td>値5</td><td colspan="2">値6</td><td rowspan="2">値7</td></tr>
<tr><th>行見出し2</th><th>行見出し6</th><td>値8</td><td>値9</td><td>値10</td></tr>
<tr><th>行見出し3</th><th>行見出し7</th><td>値11</td><td>値12</td><td>値13</td><td>値14</td></tr>
</tbody>
</table>
検証結果
パターン5(caption 要素あり/ th 要素あり/ scope 属性あり/ 見出しが複数列・行 / 結合あり )
検証目的
検証目的: 複数列・行の見出しの場合のscope属性の対応及び対応状況の確認。パターン4との挙動の違いの確認。
期待される挙動: 値セルにフォーカスがあったタイミングでscope属性で指定された適切な複数の th 要素の見出しを全て読み上げる。scope属性で関連付けられていない値セルで当該見出しを読み上げない。
検証サンプル
<!-- 「列見出し0」を列見出しとしてscope属性"col"で指定-->
<table>
<caption style="caption-side: bottom;">パターン5-1<列見出し指定>(caption 要素あり/ th 要素あり/ scope 属性あり/ 見出しが複数列・行 / 結合あり )</caption>
<tbody>
<tr><th rowspan="3" colspan="3" scope="col">列見出し0</th><th rowspan="2" scope="row" >行見出し1</th><th scope="row" >行見出し2</th><td>値1</td><td>値2</td></tr>
<tr><th scope="row" >行見出し3</th><td>値3</td><td>値4</td></tr>
<tr><th scope="row" >行見出し4</th><th>行見出し5</th><td>値5</td><td>値6</td></tr>
<tr><th colspan="2" scope="col">列見出し1</th><th scope="col">列見出し2</th><td>値7</td><td>値8</td><td>値9</td><td>値10</td></tr>
<tr><th scope="col">列見出し3</th><th scope="col">列見出し4</th><th scope="col">列見出し5</th><td>値11</td><td>値12</td><td>値13</td><td>値14</td></tr>
<tr><td>値15</td><td>値16</td><td>値17</td><td>値18</td><td>値19</td><td>値20</td><td>値21</td></tr>
<tr><td>値21</td><td colspan="2">値22と値23の結合</td><td>値24</td><td>値25</td><td>値26</td><td>値27</td></tr>
</tbody>
</table>
<!-- 「行見出し0」を行見出しとしてscope属性"row"で指定-->
<table>
<caption style="caption-side: bottom;">パターン5-2<行見出し指定>(caption 要素あり/ th 要素あり/ scope 属性あり/ 見出しが複数列・行 / 結合あり )</caption>
<tbody>
<tr><th rowspan="3" colspan="3" scope="row">行見出し0</th><th rowspan="2" scope="row" >行見出し1</th><th scope="row" >行見出し2</th><td>値1</th><td>値2</td></tr>
<tr><th scope="row" >行見出し3</th><td>値3</td><td rowspan="2">値4と値6の結合</td></tr>
<tr><th scope="row" >行見出し4</th><th>行見出し5</th><td>値5</td></tr>
<tr><th colspan="2" scope="col">列見出し1</th><th scope="col">列見出し2</th><td>値7</td><td>値8</td><td>値9</td><td>値10</td></tr>
<tr><th scope="col">列見出し3</th><th scope="col">列見出し4</th><th scope="col">列見出し5</th><td>値11</td><td>値12</td><td>値13</td><td>値14</td></tr>
<tr><td>値15</td><td>値16</td><td>値17</td><td>値18</td><td>値19</td><td>値20</td><td>値21</td></tr>
<tr><td>値21</td><td>値22</td><td>値23</td><td>値24</td><td>値25</td><td>値26</td><td>値27</td></tr>
</tbody>
</table>
検証結果
パターン6(caption 要素あり/ th 要素あり/ scope 属性なし/ 見出しが複数列・行 / 結合あり / headers 要素あり)
検証目的
検証目的: headers属性の対応及び対応状況の確認。特に値セルが結合した場合の状況確認。また、header属性に対応している場合において、headers属性による見出し指定とscope属性による見出し指定に違いがある場合の優先順位の確認
期待される挙動: 値セルにフォーカスがあったタイミングでheaders属性で指定したth 要素の見出しを読み上げる。headers属性で指定した見出しとscope属性で指定された見出しが異なる場合は、headers属性が優先される。
検証サンプル
<table>
<caption style="caption-side: bottom;">パターン6 (caption 要素あり/ th 要素あり/ scope 属性あり/ 見出しが複数列・行 / 結合あり / headers 要素あり)</caption>
<thead>
<tr><th rowspan="2" colspan="2" id="a1">列見出し1</th><th id="a2">列見出し2</th><th id="a3">列見出し3</th><th colspan="2" id="a4">列見出し4</th></tr>
<tr><th id="a5">列見出し5</th><th id="a6">列見出し6</th><th id="a7">列見出し7</th><th id="a8">列見出し8</th></tr>
</thead>
<tbody>
<tr><th rowspan="2" id="b1">行見出し1</th><th id="b4">行見出し4</th><td>値1</td><td>値2</td><td>値3</td><td headers="a2 a6 b2 b7">値4</td></tr>
<!--「値4」セルに「列見出し2」、「列見出し6」、「行見出し2」、「行見出し7」をこの順でheaders属性で関連付け(本当は「列見出し4」、「列見出し8」「行見出し1」「行見出し4」を関連付けべきであるが、あえて間違った見出しと関連付けて、挙動を確認)-->
<tr><th id="b5">行見出し5</th><td>値5</td><td colspan="2" headers="b1 b5 a3 a6 a4 a7">値6</td><td rowspan="2" headers="a4 a8 b1 b5 b2 b6">値7</td></tr>
<!--「値6」セルに「列見出し3」、「列見出し6」、「列見出し4」、「列見出し7」、「行見出し1」、「行見出し5」をこの順でheaders属性で関連付け-->
<!--「値7」セルに「列見出し4」、「列見出し8」、「行見出し1」、「行見出し5」、「行見出し2」、「行見出し6」をこの順でheaders属性で関連付け-->
<tr><th id="b2">行見出し2</th><th id="b6">行見出し6</th><td>値8</td><td>値9</td><td header="b2 b6">値10</td></tr>
<!--「値10」セルに「行見出し2」、「行見出し6」をこの順でheaders属性で関連付け-->
<tr><th id="b3">行見出し3</th><th id="b7">行見出し7</th><td>値11</td><td>値12</td><td>値13</td><td>値14</td></tr>
</tbody>
</table>
検証結果