Webパーツ辞典

ソート可能なテーブル見出しSortable table header

表の列見出しを押すと、その列で行を並べ替えられるもの。見出しの横の三角で、どの列をどちら向きに並べているかを示す。

表示例

果物の価格(見出しを押すと並べ替え)
りんご120円8
みかん180円3
ぶどう450円5

アイコン: Font Awesome 7 Free(fa-solid / fa-regular)

別名・英語表記

  • ソートボタン
  • 並べ替え見出し
  • 並び替えボタン
  • ソート列
  • 昇順・降順の切り替え
  • ソート可能な表
  • Sortable table header

実装例

<table class="x-sortable-header">
  <caption class="x-sortable-header__c">果物の価格(見出しを押すと並べ替え)</caption>
  <thead>
    <tr>
      <th scope="col">
        <button class="x-sortable-header__b" type="button">
          品名<i class="fa-solid fa-sort" aria-hidden="true"></i>
        </button>
      </th>
      <!-- aria-sort を付けるのは、いま並べ替えている1列だけ -->
      <th scope="col" aria-sort="ascending" class="x-sortable-header__on">
        <button class="x-sortable-header__b" type="button">
          価格<i class="fa-solid fa-sort-up" aria-hidden="true"></i>
        </button>
      </th>
      <th scope="col">
        <button class="x-sortable-header__b" type="button">
          在庫<i class="fa-solid fa-sort" aria-hidden="true"></i>
        </button>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr><th scope="row">りんご</th><td>120円</td><td>8</td></tr>
    <tr><th scope="row">みかん</th><td>180円</td><td>3</td></tr>
    <tr><th scope="row">ぶどう</th><td>450円</td><td>5</td></tr>
  </tbody>
</table>

AIへの指示文

「ソート可能なテーブル見出し(Sortable table header)」を実装してください。

## これは何か
表の列見出しを押すと、その列で行を並べ替えられるもの。見出しの横の三角で、どの列をどちら向きに並べているかを示す。

## 使いどころ
- 価格・日付・件数のように、利用者ごとに見たい順番が違う表
- 行数が多く、上から読むより並べ替えて探したほうが速いとき

## 実装の要件
- table > caption + thead > tr > th[scope=col][aria-sort] > button(列名 + 向きのアイコン)
- 並べ替えていない列には上下両向きのアイコン、並べ替えている列には片向きのアイコンを出す
- ボタンはセルいっぱいに広げ、押せる範囲を広く取る
- 押せる見出しと押せない見出しを見た目で区別する。全部の列に並べ替えを付ける必要はない
- 数値は数として比べる。文字列のまま比べると「1,000円」が「200円」より前に来る
- 漢字の名前は、文字コードの順に並べても読みの順にならない。並べ替え用に読み(ふりがな)を別に持つ
- ページ送りのある表は、全件を並べ替えてから切り分ける。いま見えている1ページだけを並べ替えない
- 押すたびに「昇順 → 降順」を繰り返すのか、「元の順」に戻す段階を挟むのかを決め、サイト内の表で揃える

## アクセシビリティの要件
- 見出しのセル th の中に button を置く。th そのものにクリックを付けると、キーボードで届かず、押せることも伝わらない
- 並べ替えている列の th に aria-sort="ascending" か "descending" を付ける。付けるのは同時に1列だけで、他の列からは外す
- 向きは三角や矢印の形で示し、色だけに頼らない。アイコンは aria-hidden="true" にして、向きは aria-sort で伝える
- aria-sort の変化を読み上げない環境があるので、並べ替えたあとに「価格の安い順に並べ替えました」を aria-live="polite" で伝えると確実
- 並べ替えたあとも、フォーカスは押した見出しのボタンに残す

## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。

```html
<table class="x-sortable-header">
  <caption class="x-sortable-header__c">果物の価格(見出しを押すと並べ替え)</caption>
  <thead>
    <tr>
      <th scope="col">
        <button class="x-sortable-header__b" type="button">
          品名<i class="fa-solid fa-sort" aria-hidden="true"></i>
        </button>
      </th>
      <!-- aria-sort を付けるのは、いま並べ替えている1列だけ -->
      <th scope="col" aria-sort="ascending" class="x-sortable-header__on">
        <button class="x-sortable-header__b" type="button">
          価格<i class="fa-solid fa-sort-up" aria-hidden="true"></i>
        </button>
      </th>
      <th scope="col">
        <button class="x-sortable-header__b" type="button">
          在庫<i class="fa-solid fa-sort" aria-hidden="true"></i>
        </button>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr><th scope="row">りんご</th><td>120円</td><td>8</td></tr>
    <tr><th scope="row">みかん</th><td>180円</td><td>3</td></tr>
    <tr><th scope="row">ぶどう</th><td>450円</td><td>5</td></tr>
  </tbody>
</table>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-sortable-header,
.x-sortable-header *:not([class*="fa-"]),
.x-sortable-header *:not([class*="fa-"])::before,
.x-sortable-header *:not([class*="fa-"])::after {
  box-sizing: border-box;
  /* 貼った先の既定の書体に引きずられないようにする。書体が変わると
     文字の幅と高さが変わり、枠のサイズまでずれる */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}

.x-sortable-header {
  width: 270px;
  border-collapse: collapse;
  background: #fff;
  color: #1b2028;
  font-size: 12px;
}
.x-sortable-header__c {
  margin-bottom: 6px;
  color: #5b6472;
  font-size: 11px;
  text-align: left;
}
.x-sortable-header th,
.x-sortable-header td {
  padding: 7px 10px;
  border: 1px solid #e3e6ea;
  text-align: left;
}
.x-sortable-header td {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* 見出しのセルは余白を持たず、中のボタンがセルいっぱいに広がる(押せる範囲を広く取る) */
.x-sortable-header thead th {
  padding: 0;
  background: #f1f3f6;
}
.x-sortable-header__b {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  min-height: 34px;
  padding: 0 10px;
  border: 0;
  background: none;
  color: #1b2028;
  font: inherit;
  font-size: 11.5px;
  font-weight: 700;
  cursor: pointer;
}
.x-sortable-header__b .fa-solid {
  width: auto;
  color: #5b6472;
}
.x-sortable-header__b:hover {
  background: #e3e8ef;
}
.x-sortable-header__b:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: -2px;
}
/* 並べ替え中の列。向きは矢印の形で示し、色と下線は補助にする */
.x-sortable-header__on {
  box-shadow: inset 0 -2px 0 #1d4ed8;
}
.x-sortable-header__on .x-sortable-header__b .fa-solid {
  color: #1d4ed8;
}
```

## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。

使いどころ

  • 価格・日付・件数のように、利用者ごとに見たい順番が違う表
  • 行数が多く、上から読むより並べ替えて探したほうが速いとき

注意点

  • 押せる見出しと押せない見出しを見た目で区別する。全部の列に並べ替えを付ける必要はない
  • 数値は数として比べる。文字列のまま比べると「1,000円」が「200円」より前に来る
  • 漢字の名前は、文字コードの順に並べても読みの順にならない。並べ替え用に読み(ふりがな)を別に持つ
  • ページ送りのある表は、全件を並べ替えてから切り分ける。いま見えている1ページだけを並べ替えない
  • 押すたびに「昇順 → 降順」を繰り返すのか、「元の順」に戻す段階を挟むのかを決め、サイト内の表で揃える

アクセシビリティの要点

  • 見出しのセル th の中に button を置く。th そのものにクリックを付けると、キーボードで届かず、押せることも伝わらない
  • 並べ替えている列の th に aria-sort="ascending" か "descending" を付ける。付けるのは同時に1列だけで、他の列からは外す
  • 向きは三角や矢印の形で示し、色だけに頼らない。アイコンは aria-hidden="true" にして、向きは aria-sort で伝える
  • aria-sort の変化を読み上げない環境があるので、並べ替えたあとに「価格の安い順に並べ替えました」を aria-live="polite" で伝えると確実
  • 並べ替えたあとも、フォーカスは押した見出しのボタンに残す

作るときに使えるツール

関連するパーツ