テーブルtableタグのヘッダー行・列(thタグ)を固定する方法
tableデータの量が多い場合、th見出しタグを固定すればスクロール時にデータを把握しやすくなります。
thタグは縦方向・横方向・複数見出しの固定が可能です。
一般的にはposition: sticly;が使われますが、flexboxを使った固定方法もあります。
tableの見出しタグを固定する
本記事では、tableタグの見出し部分にあたる「thタグ」を固定する方法をご紹介します。
これはtableタグ内のデータ量が増えた場合に有効な手段ですよね。
データの表示枠をスクロールさせても、見出しの位置(縦列もしくは横列)が固定されるので、データが把握しやすくなります。
固定するタグ
tableソースを構成するタグには、table、thead、tbody、tr、th、tdなど様々ありますね。
この中で見出しに使うのはtheadタグ(グループ化タグ)や、thタグです。
固定をする場合、一般的には「thタグ」にCSSスタイルを適用する事で実現します。
thを固定する理由
本来はtheadが見出し部分をグループ化するためのタグですので、このtheadを固定する方が良いと思います。
しかし現状はそうせずに、中にあるthタグを固定するのがセオリーです。
理由としては以下の事が挙げられます。
・trやtheadを固定する事ができない事(一般的なブラウザでは不可)
・列の幅を固定しなければならないなど各種制約が増える(CSS量が増える)
本記事では2つの方法を紹介
一般的にはposition: sticky;をthタグに適用し、top: 0;で固定する形になります。
本記事ではそれ以外に「flexbox」を使った固定手段もご紹介します。
ヘッダー列(横列)の固定化
ページをスクロールすると、一番上の見出し列が固定される一般的な方法です。
htmlソース
<table class="st-tbl1"> <thead> <tr class="red"> <th>見出し1</th> <th>見出し2</th> <th>見出し3</th> <th>見出し4</th> <th>見出し5</th> </tr> </thead> <tbody> <tr> <td>データ1</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> </tr> …以下データ繰り返し </tbody> </table>
CSS
table.st-tbl1 { text-align: left; position: relative; border-collapse: collapse; } table.st-tbl1 th, table.st-tbl1 td{ padding: 1rem; border: solid 1px #ddd; } table.st-tbl1 th { background: white; position: sticky; top: 0; }
CSS上では設定していませんが、見出しタグへ色(.red)をつけています。
サンプル結果
見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 |
---|---|---|---|---|
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
この様スクロールすると、ヘッダーが固定追随します。
tableをdiv枠などで囲ってその枠の高さを固定すれば、スクロール時にその枠の上部に固定できますね。
複数列ある見出しの固定化
次は上記の応用版です。
それぞれジャンル別に存在する複数のth見出しが、スクロールするごとに固定されるパターンです。
htmlソース
<table class="st-tbl1"> <tr class="red"> <th>見出し1</th> <th>見出し2</th> <th>見出し3</th> <th>見出し4</th> <th>見出し5</th> </tr> <tr> <td>データ1</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> </tr> …以下データ繰り返し <tr class="green"> <th>見出し1</th> <th>見出し2</th> <th>見出し3</th> <th>見出し4</th> <th>見出し5</th> </tr> <tr> <td>データ1</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> </tr> …以下データ繰り返し <tr class="purple"> <th>見出し1</th> <th>見出し2</th> <th>見出し3</th> <th>見出し4</th> <th>見出し5</th> </tr> <tr> <td>データ1</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> </tr> …以下データ繰り返し </table>
theadやtbodyは特に使っていません。
CSS
table.st-tbl1 { text-align: left; position: relative; border-collapse: collapse; } table.st-tbl1 th, table.st-tbl1 td{ padding: 1rem; border: solid 1px #ddd; } table.st-tbl1 tr.red th { background: red; color: white; } table.st-tbl1 tr.green th { background: green; color: white; } table.st-tbl1 tr.purple th { background: purple; color: white; } table.st-tbl1 th { background: white; position: sticky; top: 0; }
trタグへのクラス名設置
HTMLソース上ではtrタグにredやgreenなどのクラスを付けています。
本来trタグはCSS制御が効かないのですが、クラスの付いた子要素のth全てに対しデザイン指定ができます。
それによりthタグに直接クラス名を付ける必要が無くなり、効率よくなります。
サンプル結果
見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 |
---|---|---|---|---|
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
データ1 | データ2 | データ3 | データ4 | データ5 |
スクロールすると、それぞれ色のついた見出し部分が順に固定されていきます。
列・行(横1列・縦1列)両方の固定化
このパターンでは縦をスクロールすると見出し列が固定され、横スクロールすると左の見出し行が固定されます。
わかりやすくするため、表示枠を固定(iframeで固定)し、縦横にスクロールバーを付けています。
htmlソース
<table class="st-tbl1"> <thead> <tr> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> <th>見出し</th> </tr> </thead> <tbody> <tr> <th>縦軸</th> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> <td>データ</td> </tr> …以下データ繰り返し </tbody> </table>
tbodyタグ内のデータは一番左のみthタグが入っていますね。
CSS
table.st-tbl1{ width: 1000px; /*見切れるように1000pxにしている*/ } .st-tbl1 thead th { /* 縦スクロール時を固定 */ position: sticky; top: 0; /* tbody内のセルより手前に表示する */ z-index: 1; } .st-tbl1 th:first-child { /* 横スクロールを固定 */ position: sticky; left: 0; background: #f1f1fd; } .st-tbl1 thead th:first-child { /* 一番左端のthead thが横スクロール時に隠れない様に */ z-index: 2; background: #424242; } .st-tbl1 th, .st-tbl1 td{ border-collapse: collapse; text-align: left; padding: .2rem .5rem; font-weight: normal; } .st-tbl1 thead th { background: #424242; color: #E0E0E0; }
縦スクロール時の見出しの固定解説
まず横並び(列)のthead内thタグ「thead th」に対して、固定を設定します。
.st-tbl1 thead th { position: sticky; top: 0; z-index: 1; }
さらにtbody内のセルより手前(上)に表示するために「z-index: 1;」を追加します。
これにより縦スクロール時にデータのセルがthの下に隠れるようになります。
横スクロール時の左端行の固定解説
th:first-childで各行の一番最初のthのみに適用する事で、各列の一番左端の行を固定する事ができます。
.st-tbl1 th:first-child { position: sticky; left: 0; background: #f1f1fd; }
さらに横スクロール時に一番上の見出しが隠れないよう、thead th:first-childにz-index: 2;を入れて、色も付けています。
サンプル結果
縦と横のスクロールバーを動かすと、上部の見出し・左端の見出しがそれぞれ固定されます。
position: sticky;の注意
「position: sticky;」で簡単に固定スクロールする事ができますが、IEやSafariでは上手く動かない様です。
Chromeや新Edge、FireFoxでは問題なく動作します。
flexboxによる固定化
このサンプルはtableレイアウトをflexboxで構成しています。
htmlソース上ではthタグは横並びに記述されますが、ページ上では各行左端の「見出し」として縦並びになります。
Flexboxの動作を利用して、テーブルの各行を列に変換した例をご紹介します。
htmlソース
<div class="st-tbl1"> <table> <thead> <tr> <th>縦軸1</th> <th>縦軸2</th> <th>縦軸3</th> <th>縦軸4</th> <th>縦軸5</th> <th>縦軸6</th> <th>縦軸7</th> <th>縦軸8</th> </tr> </thead> <tbody> <tr> <td class="text">Aデータ</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> <td>データ6</td> <td>データ7</td> <td>データ8</td> </tr> <tr> <td class="text">Bデータ</td> <td>データ2</td> <td>データ3</td> <td>データ4</td> <td>データ5</td> <td>データ6</td> <td>データ7</td> <td>データ8</td> </tr> …以下Cデータ・Dデータ繰り返し </tbody> </table> </div>
通常上記の様なHTMLだと、theadの段の下にtbodyが来てthやtdは横並びになるはずですよね。
この状態でCSSのflexboxを使うと、行と列を入れ替える事ができる様になります。
CSS
.st-tbl1 th { background-color: #555; color: #fff; white-space: nowrap; } .st-tbl1 th, .st-tbl1 td { text-align: left; padding: 0.5em 1em; white-space: nowrap; } .st-tbl1 table { display: flex; } .st-tbl1 tbody { display: flex; position: relative; overflow-x: auto; overflow-y: hidden; } .st-tbl1 th, .st-tbl1 td { display: block; } .text{ background: #f1f1fd; }
ここでdisplay: flex;が使われているのは、tableタグ自体とtbodyタグですね。
そしてthやtdは全てdisplay: block;としてあります。この2つがポイントです。
サンプル結果
縦軸1 | 縦軸2 | 縦軸3 | 縦軸4 | 縦軸5 | 縦軸6 | 縦軸7 | 縦軸8 |
---|---|---|---|---|---|---|---|
Aデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Bデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Cデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Dデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Eデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Fデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Gデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Hデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
Iデータ | データ2 | データ3 | データ4 | データ5 | データ6 | データ7 | データ8 |
上記を見てわかる通り、横並びのはずのth「縦軸」が縦に並び、tbody内trが列として横に並んでいます。
flexboxによるtable構成の解説
flex化する事で子要素が横並びになる仕様を利用する
tableに「display: flex;」を適用する事により、その直下となるtheadタグとtbodyタグに対して「flexの子要素」が付与されます。
つまりtheadの区画とtbodyの区画が「横並び」に並ぶわけです。
さらにtbodyタグ要素に対してもdisplay: flex;とする事で、その中にあるtrタグ要素がさらなるフレックスの子要素として横並びで配置される訳です。
Aデータ・Bデータが全て横並びなのは、tbody内のtrが横並びしている状態です。
その上で全てのthやtdのデータセルにdisplay: block;を設定しているので、thセル・tdセルが縦に並んでいるのです。
メリット・デメリット
この手法の利点は、ユーザーがデータの列を横スクロールする際、theadタグのヘッダーが常に固定され見失われません。
ただこの手法を使うと、視覚的な順序とソースの順序に不一致が生じてわかりにくくなる点があります。