少しアクセシブルな、列や行を固定した表

大きな表では見出しになる列や行を固定して、残りの行や列をスクロールさせた方がわかりやすい場合があります。しかし、行や列を固定した表をアクセシブルに実装することが難しいことも事実です。

現状では、固定部分とスクロール可能部分をそれぞれ別のtable要素にするのが一般的だと思います。その場合、固定部分のセルとスクロール可能部分のセルの関係性は崩れてしまいます。td要素にはheaders属性がありますが、同じtable要素内のth要素しか参照できません(HTML5など)。そのため、複数の表をまたいで見出しセルとデータセルを関連づけることもできません。何より、複数の表を1つの表に見えるように、行の高さや列の幅をJavaScriptで調整していることが、関係性が崩れていることをあらわしています。

しかし、position:stickyを使えば、表を分割せずに行や列を固定できます。

position:stickyを使って列や行を固定した表の例

例えば、行を固定するには次のように書くことができるでしょう。

<div class="scrollable sticky-rows">
<table>
 <thead class="sticky row">
 <tr><th>Name<th>Description<th>Legacy code exception field value (if any)
 <tbody>
 <tr>
  <td>"IndexSizeError"
  <td>The index is not in the allowed range.
  <td><code>INDEX_SIZE_ERR (1)
  <!-- 略 -->
</table>
</div>

.scrollable {
    width: -moz-fit-content;
    width: -webkit-fit-content;
    width: fit-content;
}

.scrollable.sticky-rows {
    overflow-y: scroll;
    max-height: 100%;/* 幅は成り行き */
}

.scrollable .sticky {
    position: sticky;
}

.scrollable .sticky.row {
    top: 0;
}

列を固定するには次のように書けるでしょう。

<div class="scrollable sticky-cols">
<table>
 <thead>
 <tr><th class="sticky col">Name<th>Description<th>Legacy code exception field value (if any)
 <tbody>
 <tr>
  <td class="sticky col">"IndexSizeError"
  <td>The index is not in the allowed range.
  <td><code>INDEX_SIZE_ERR (1)
  <!-- 略 -->
</table>
</div>

.scrollable.sticky-cols {
    overflow-x: scroll;
    max-width: 100%;/* 高さは成り行き */
}

.scrollable.sticky-cols > table {
    width: -moz-max-content;
    width: -webkit-max-content;
    width: max-content;
}

.scrollable .sticky {
    position: sticky;
}

.scrollable .sticky.col {
    left: 0;
}

両方固定するなら、次のようになります。

<div class="scrollable sticky-rows sticky-cols">
<table>
 <thead class="sticky row">
 <tr><th class="sticky col">Name<th>Description<th>Legacy code exception field value (if any)
 <tbody>
 <tr>
  <td class="sticky col">"IndexSizeError"
  <td>The index is not in the allowed range.
  <td><code>INDEX_SIZE_ERR (1)
  <!-- 略 -->
</table>
</div>

.scrollable {
    width: -moz-fit-content;
    width: -webkit-fit-content;
    width: fit-content;
}

.scrollable.sticky-rows {
    overflow-y: scroll;
    max-height: 100%;
}

.scrollable.sticky-cols {
    overflow-x: scroll;
    max-width: 100%;
}

.scrollable.sticky-cols > table {
    width: -moz-max-content;
    width: -webkit-max-content;
    width: max-content;
}

.scrollable .sticky {
    position: sticky;
}

.scrollable .sticky.row {
    top: 0;
    z-index: 1;/* 列よりも行を優先して表示 */
}

.scrollable .sticky.col {
    left: 0;
}

この例は、Chrome Canary(33.0.1750.117)でposition:stickyを有効にすると、先頭行や先頭列を固定した表として動作します。Firefox Nightly(30.0a1 (2014-02-21))では動作しません。

overflowプロパティなど

この例ではスクロールできる領域をつくるためにdiv要素を追加しています。これは、displayがtableの要素にoverflowプロパティなどを指定しても効果がないためです。このことはCSS 2.1では明言されていませんでしたが、CSS 2.1に対するErrata(s.11.1.1b)で明言されています。scrllとautoに対して次の文言が追加されています。

When used on table boxes, this value has the same meaning as 'visible'.

来たるCSS 2.2にも反映されることでしょう。

表関連の要素に対するposition

また、この例はChromeでは動作し、Firefoxでは動作しません。

CSS Positioned Layout Module Level 3(2012年2月の草案)では、position:stickyの表関連に関する効果はposition:relativeと同じであるとされています。CSS Positioned Layout Module Level 3のposition:relativeでは次のように述べられています。

table-row-group, table-header-group, table-footer-group and table-row offset relative to its normal position within the table. If table-cells span multiple rows, only the cells originating in the relative positioned row is offset.

日本語にすると次のようになるでしょうか。

table-row-group、table-header-group、table-footer-groupおよびtable-rowは、表の範囲内で通常の位置から相対的に移動する。もしtable-cellが複数の行にわたっている場合、相対配置されている行に由来するセルだけが移動する。

しかし、CSS 2.1では表関連の要素に対するposition:relativeの効果は未定義とされています(CSS 2.1におけるpositionプロパティ)。

The effect of 'position:relative' on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.

今のところ、Firefoxではposition:relativeは表関連の要素には効果がありません。position:stickyも同様です。これはCSS 2.1を前提にしていれば問題とならない挙動ですが、CSS Positioned Layout Module Level 3への対応を進めてもらいたいものです。

Chromeではposition:relativeを指定したtable-cellはtopプロパティなどで位置を変更できます。しかし、table-row-groupやtable-rowにposition:relativeを指定してもtopプロパティなどで位置を変更できません。

一方、position:sitckyをtable-cellに指定するとtopプロパティなどで位置を変更できて、かつ、位置を固定できます。table-row-groupやtable-rowにposition:relativeを指定すると、topプロパティなどで位置を変更することはできませんが、位置を固定できます。

なお、CSS Positioned Layout Module Level 3のpositionプロパティはtable-column-groupやtable-columnに適用されないとあります。

まとめ

position:stcikyが広く実装されれば、表の行や列を固定するためにtable要素を分割せずに済むようになるでしょう。

関連情報