ヘッダーセル固定で横(縦)スクロールするTableを作成するCSS

左端のセルが固定された、横スクロールのTableをHTMLとCSSで作りたい。Excelの「ウィンドウ枠の固定」みたいなやつ。

こんなやつです。↓

条件

  • 左端のセルを固定して横スクロールするテーブル
  • スクロール可能であることがわかるよう、スクロール可能時はテーブルの右側の枠線を表示しない。終点までスクロールすると枠線が現れる
  • なるべく単純でわかりやすく

実際のコード

<div class="sticky-x">
  <table>
    <tbody>
      <tr>
        <th>laborum</th>
        <td>Lorem veniam nostrud in</td>
        <td>proident sunt in exercitation officia in</td>
        <td>commodo anim excepteur eiusmod ut</td>
      </tr>
      <tr>
        <th>Reprehenderit</th>
        <td>aliqua enim anim consectetur incididunt</td>
        <td>incididunt</td>
        <td>aute non nulla laboris</td>
      </tr>
      <tr>
        <th>laborum nostrud quis elit nulla</th>
        <td>ad labore labore voluptate</td>
        <td>est consectetur</td>
        <td>labore cupidatat proident tempor ea</td>
      </tr>
    </tbody>
  </table>
</div>

以下はCSS。必須とコメントしてある部分はSticky(固定スクロール)の実装に必ず必要です。tdwhite-space: nowrap は自動改行をさせないもの。必要に応じて削除してください。

.sticky-x {
  --border-color: #e0e0e5;
  --stripe-bg-color: #f5f5f8;
  --header-bg-color: #e7e7ec;

  overflow-x: scroll; /* 必須! */
  border: 1px solid var(--border-color);
  border-right: none;
}

table {
  border-collapse: collapse;
  width: 100%;
}

tr:not(:last-child) {
  border-bottom: 1px solid var(--border-color);
}

tr:nth-of-type(odd) {
  background: var(--stripe-bg-color);
}

th,
td {
  padding: 16px 8px;
  text-align: left;
}

th {
  position: sticky; /* 必須! */
  left: 0; /* 必須! */
  background: var(--header-bg-color);
}

td {
  white-space: nowrap; /* 任意 */
  border-right: 1px solid var(--border-color);
}

実際のコードはCodeSandboxでも確認できます↓

CodeSandboxで開く

縦も横もStickyなTable

縦も横もStickyにするには、ちょっとだけコツがあります。

それは、いちばん左上のセルの z-index 。ほかのヘッダーセルより高いz-indexの値を指定してあげましょう。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>縦横StickyなTable</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <h1>縦横StickyなTable Theadあり</h1>
    <div class="sticky-table-xy">
      <table>
        <thead>
          <tr>
            <th>est</th>
            <th>nostrud nisi</th>
            <th>excepteur</th>
            <th>proident</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <th>laborum</th>
            <td>Lorem veniam nostrud in</td>
            <td>proident sunt in exercitation officia in</td>
            <td>commodo anim excepteur eiusmod ut</td>
          </tr>
          <tr>
            <th>Reprehenderit</th>
            <td>aliqua enim anim consectetur incididunt</td>
            <td>incididunt</td>
            <td>aute non nulla laboris</td>
          </tr>
          <tr>
            <th>laborum nostrud quis elit nulla</th>
            <td>ad labore labore voluptate</td>
            <td>est consectetur</td>
            <td>labore cupidatat proident tempor ea</td>
          </tr>

          <!-- 以下略 -->

        </tbody>
      </table>
    </div>
  </body>
</html>
.sticky-table-xy {
  --border-color: #e0e0e5;
  --stripe-bg-color: #f5f5f8;
  --stripe-header-bg-color: #e7e7ec;
  /* --header-bg-color: #e7e7ec; */
  --header-bg-color: #f5f5f8;

  overflow: scroll; /* 必須! */
  border: 1px solid var(--border-color);
  border-right: none;
  height: 70svh;
}

table {
  border-collapse: collapse;
  width: 100%;
}

th,
td {
  padding: 16px 8px;
  text-align: left;
}

th {
  position: sticky; /* 必須 */
  border-right: 1px solid var(--border-color);
}

td {
  white-space: nowrap; /* 任意 */
  border-right: 1px solid var(--border-color);
}

thead tr {
  border-bottom: 1px solid var(--border-color);
}

thead th {
  top: 0; /* 必須 */
  z-index: 2; /* 必須 */
  background: var(--header-bg-color);
}

thead th:first-child {
  left: 0; /* 必須  */
  z-index: 3; /* 必須 */
  background: var(--stripe-header-bg-color);
}

thead th:last-child {
  border-right: 1px solid var(--border-color);
}

tbody tr:not(:last-child) {
  border-bottom: 1px solid var(--border-color);
}

tbody tr:nth-of-type(even) {
  background: var(--stripe-bg-color);
}

tbody tr:nth-of-type(even) th {
  background: var(--stripe-header-bg-color);
}
tbody th {
  left: 0; /* 必須! */
  z-index: 2; /* 必須 */
  background: var(--header-bg-color);
}

/* 装飾用 */
body {
  margin: 0;
  padding: 24px;
}