tableの見出しを固定してスクロールする方法

HTMLの<table>で作成した表の見出し部分だけを固定して、内容部分をスクロールできるようにする方法の備忘録です。
上の見出しを固定
表の上部分の見出しを固定して、内容部分を上下にスクロールする方法です。
| 見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 | 見出し6 | 見出し7 |
|---|---|---|---|---|---|---|
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
サンプルの表示では、overflow: scroll;をつけた<div>タグで囲うことで、グレーの枠の中でスクロールさせています。<table>のまま使用すると、表示している画面の一番上で見出しが固定されます。
HTML
<table>
<tbody>
<tr>
<th>見出し1</th>
<th>見出し2</th>
<th>見出し3</th>
<th>見出し4</th>
<th>見出し5</th>
<th>見出し6</th>
<th>見出し7</th>
</tr>
<tr>
<td>セル</td>
<td>セル</td>
… 省略 …
<td>セル</td>
<td>セル</td>
</tr>
</tbody>
</table>
1行目の<tr>に、<th>で固定する見出しを入れています。
CSS
table {
width: 100%;
position: relative;
}
th, td {
border: none;
white-space: nowrap;
}
th {
font-weight: bold;
color: #fff;
background: #333;
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
tr:nth-child(odd) td {
background: #f3f5f5;
}
余白やデザインに関わる一部のコードは割愛しています。
親要素となるtableにposition: relative;を設定して、見出し部分のthにposition: sticky;を設定することで表示位置を固定します。
見出しの固定位置はtop: 0;、left: 0;で表の上部に設定します。さらに、z-index: 1;を指定して内容部分の上に重ねます。
枠線のあるtableの場合
borderで枠線をつけた<table>の場合、そのままでは見出し部分の線がスクロール時に消えてしまうので、border-collapse: separate;を指定してセルごとにborderを設定します。
| 見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 | 見出し6 | 見出し7 |
|---|---|---|---|---|---|---|
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
| セル | セル | セル | セル | セル | セル | セル |
CSS
table {
border-collapse: separate;
border-spacing: 0;
width: 100%;
position: relative;
}
th, td {
border-right: 1px solid #333;
border-bottom: 1px solid #333;
white-space: nowrap;
}
tr:first-child th,
tr:first-child td {
border-top: 1px solid #333;
}
th:first-child,
td:first-child {
border-left: 1px solid #333;
}
th {
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
<table>にborder-collapse: separate;、border-spacing: 0;を設定して、各セルにborder-right、border-bottomで枠線をつけます。さらに:first-childを活用して外側の該当するセルにborder-top、border-leftを設定して枠線のある表を作成します。
左の見出しを固定
上下のスクロールと同様に表の左部分の見出しを固定して、内容部分を左右にスクロールできます。
| 見出し1 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 見出し2 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 見出し3 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 見出し4 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 見出し5 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
HTML
<table>
<tbody>
<tr>
<th>見出し1</th>
<td>セル</td>
<td>セル</td>
…
<td>セル</td>
<td>セル</td>
</tr>
… 省略 …
<tr>
<th>見出し5</th>
<td>セル</td>
<td>セル</td>
…
<td>セル</td>
<td>セル</td>
</tr>
</tbody>
</table>
各行の先頭の<th>を見出しとして固定します。
CSS
table {
width: 100%;
position: relative;
}
th, td {
border: none;
white-space: nowrap;
}
th {
font-weight: bold;
color: #fff;
background: #333;
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
tr:nth-child(odd) td {
background: #f3f5f5;
}
th {
position: sticky;
left: 0;
z-index: 1;
}
左右のスクロールの場合はposition: sticky;にleft: 0;だけを指定します。
セルのテキストが意図せぬ位置で改行しないようにwhite-space: nowrap;を指定するかwidthで横幅を指定します。
縦横の見出しを固定
縦横の見出しを固定する方法です。縦スクロールの時は上の見出しを固定して、横スクロールの時は左の見出しを固定します。
| 見出し/行 | 見出し1 | 見出し2 | 見出し3 | 見出し4 | 見出し5 | 見出し6 | 見出し7 | 見出し8 | 見出し9 | 見出し10 | 見出し11 | 見出し12 | 見出し13 | 見出し14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 行1 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行2 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行3 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行4 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行5 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行6 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行7 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行8 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行9 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行10 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行11 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行12 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行13 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行14 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
| 行15 | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル | セル |
HTML
<table>
<thead>
<tr>
<th>見出し/行</th>
<th>見出し1</th>
<th>見出し2</th>
<th>見出し3</th>
… 省略 …
</tr>
</thead>
<tbody>
<tr>
<th>行1</th>
<td>セル</td>
<td>セル</td>
…
<td>セル</td>
<td>セル</td>
</tr>
… 省略 …
<tr>
<th>行15</th>
<td>セル</td>
<td>セル</td>
…
<td>セル</td>
<td>セル</td>
</tr>
</tbody>
</table>
上部の見出しは<thead>タグ内の<th>で作成します。横の見出しは<tbody>タグ内の各行の先頭<th>で作成します。
CSS
table {
width: 100%;
position: relative;
}
th, td {
border: none;
white-space: nowrap;
}
th {
font-weight: bold;
color: #fff;
background: #333;
position: sticky;
top: 0;
left: 0;
z-index: 1;
}
tr:nth-child(odd) td {
background: #f3f5f5;
}
thead th {
color: #fff;
background: #333;
position: sticky;
top: 0;
z-index: 1;
}
thead th:first-child {
position: sticky;
top: 0;
left: 0;
z-index: 2;
}
tbody th {
color: #fff;
background: #333;
position: sticky;
left: 0;
z-index: 1;
}
<thead>、<tbody>の各<th>をposition: sticky;で位置固定します。
さらに、左上のセルがどのスクロールでも動かないようにth:first-childで固定してz-index: 2;で一番上に重ねます。


