Webパーツ辞典

左右移動リストTransfer list

左右に2つの一覧が並び、間の「→」「←」ボタンで選んだ項目を行き来させる部品。表に出す列やメンバーの割り当てを選ぶ画面で見かける。

表示例

表示しない列 3件
表示する列 3件

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

別名・英語表記

  • トランスファーリスト
  • デュアルリストボックス
  • リストシャトル
  • 左右の一覧で移す
  • Transfer list

実装例

<div class="x-transfer-list">
  <fieldset class="x-transfer-list__box">
    <legend class="x-transfer-list__h">表示しない列 <span class="x-transfer-list__n">3件</span></legend>
    <!-- list-style: none の ul は Safari がリストとして読まないので role="list" を足す -->
    <ul class="x-transfer-list__list" role="list">
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="tel">電話番号</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="addr" checked>住所</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="birth">生年月日</label></li>
    </ul>
  </fieldset>
  <div class="x-transfer-list__ops">
    <!-- 左で1件以上選ばれているときだけ押せる。選択が無いときは aria-disabled="true" にする -->
    <button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示する列」へ移す">
      <i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
    </button>
    <button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示しない列」へ戻す" aria-disabled="true">
      <i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
    </button>
  </div>
  <fieldset class="x-transfer-list__box">
    <legend class="x-transfer-list__h">表示する列 <span class="x-transfer-list__n">3件</span></legend>
    <ul class="x-transfer-list__list" role="list">
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="name">氏名</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="mail">メール</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="dept">部署</label></li>
    </ul>
  </fieldset>
  <!-- 移したら「住所を表示する列へ移しました」と書き換える -->
  <p class="x-transfer-list__sr" role="status"></p>
</div>

AIへの指示文

「左右移動リスト(Transfer list)」を実装してください。

## これは何か
左右に2つの一覧が並び、間の「→」「←」ボタンで選んだ項目を行き来させる部品。表に出す列やメンバーの割り当てを選ぶ画面で見かける。

## 使いどころ
- 表に表示する列・チームに入れるメンバーのように、候補の中から「入れる側」と「入れない側」を分けて見せたいとき
- 選んだ側の一覧そのものを見ながら確かめたいとき(何件選んだか、何が残っているか)

## 実装の要件
- fieldset > legend(名前と件数)+ ul[role=list] > li > label > input[type=checkbox]、これを左右に2つ置き、間に移す button を2つ(全件を移す button を足すなら4つ)
- 移すボタンは、移せる項目が選ばれているときだけ有効にする。無効の間は aria-disabled="true" にして見た目も変える
- 送信するのは右側(選んだ側)の値だけ。並び順に意味があるなら、右側に上下の並べ替えボタンを足す
- 候補が少ないならチェックボックスを並べるだけで足りる。左右移動リストは、選んだものを別の一覧として見せる価値があるときに使う
- 複数選択プルダウンとの違いは、選んだ側がいつも見えていること。画面の幅を取るので、狭い画面では上下に積むか、別の部品にする
- 一覧の見出しに件数を出し、移すたびに更新する。移したことが件数でも分かるようにする
- 候補が多いときは各一覧の上に絞り込みの欄を置く。長い一覧をスクロールして探させない
- ドラッグで移す操作だけにしない。ボタンで移せる道を必ず残す

## アクセシビリティの要件
- 各一覧は fieldset と legend(「表示しない列」)で名前を付け、項目はチェックボックスにする。選んだ状態がそのまま読み上げに乗る
- 移すボタンには aria-label で「選んだ列を『表示する列』へ移す」のように行き先まで書く。矢印の絵だけでは向きしか伝わらない
- 選択が無いときのボタンは disabled ではなく aria-disabled="true" にする。押した直後に disabled になると、そのボタンにあったフォーカスが失われる
- 移したら role="status" の領域で「住所を表示する列へ移しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
- 選んだ行は塗りに加えてチェックの形と太字でも示す(達成基準 1.4.1 色の用途)

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

```html
<div class="x-transfer-list">
  <fieldset class="x-transfer-list__box">
    <legend class="x-transfer-list__h">表示しない列 <span class="x-transfer-list__n">3件</span></legend>
    <!-- list-style: none の ul は Safari がリストとして読まないので role="list" を足す -->
    <ul class="x-transfer-list__list" role="list">
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="tel">電話番号</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="addr" checked>住所</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="hidden" value="birth">生年月日</label></li>
    </ul>
  </fieldset>
  <div class="x-transfer-list__ops">
    <!-- 左で1件以上選ばれているときだけ押せる。選択が無いときは aria-disabled="true" にする -->
    <button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示する列」へ移す">
      <i class="fa-solid fa-chevron-right" aria-hidden="true"></i>
    </button>
    <button class="x-transfer-list__b" type="button" aria-label="選んだ列を「表示しない列」へ戻す" aria-disabled="true">
      <i class="fa-solid fa-chevron-left" aria-hidden="true"></i>
    </button>
  </div>
  <fieldset class="x-transfer-list__box">
    <legend class="x-transfer-list__h">表示する列 <span class="x-transfer-list__n">3件</span></legend>
    <ul class="x-transfer-list__list" role="list">
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="name">氏名</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="mail">メール</label></li>
      <li><label class="x-transfer-list__it"><input class="x-transfer-list__c" type="checkbox" name="shown" value="dept">部署</label></li>
    </ul>
  </fieldset>
  <!-- 移したら「住所を表示する列へ移しました」と書き換える -->
  <p class="x-transfer-list__sr" role="status"></p>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-transfer-list,
.x-transfer-list *:not([class*="fa-"]),
.x-transfer-list *:not([class*="fa-"])::before,
.x-transfer-list *: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-transfer-list {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 340px;
  max-width: 100%;
  color: #1b2028;
  font-size: 13px;
}
.x-transfer-list__box {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 1px solid #7f8a99;
  border-radius: 8px;
  background: #fff;
  overflow: hidden;
}
/* legend を枠の中の見出し帯として描く。float で枠線の切れ目を作らせない */
.x-transfer-list__h {
  float: left;
  width: 100%;
  margin: 0;
  padding: 7px 10px;
  border-bottom: 1px solid #d5dae1;
  background: #f4f6f9;
  font-size: 12px;
  font-weight: 700;
}
.x-transfer-list__n {
  color: #5b6472;
  font-weight: 400;
}
.x-transfer-list__list {
  clear: both;
  height: 118px;
  margin: 0;
  padding: 4px;
  overflow-y: auto;
  list-style: none;
}
.x-transfer-list__it {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 6px;
  border-radius: 6px;
  cursor: pointer;
}
.x-transfer-list__it:hover {
  background: #f4f6f9;
}
/* 選んだ行は塗りに加えてチェックの形でも分かる */
.x-transfer-list__it:has(:checked) {
  background: #eef2ff;
  color: #1d4ed8;
  font-weight: 700;
}
.x-transfer-list__c {
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: #1d4ed8;
}
.x-transfer-list__ops {
  display: flex;
  flex: none;
  flex-direction: column;
  gap: 8px;
  padding-top: 30px;
}
.x-transfer-list__b {
  width: 36px;
  height: 36px;
  border: 1px solid #1d4ed8;
  border-radius: 8px;
  background: #1d4ed8;
  color: #fff;
  font-size: 13px;
  cursor: pointer;
}
/* 押せないボタンも枠は残し、何のボタンかは読めるようにする */
.x-transfer-list__b[aria-disabled="true"] {
  border-color: #7f8a99;
  background: #fff;
  color: #5b6472;
  cursor: not-allowed;
}
.x-transfer-list__c:focus-visible,
.x-transfer-list__b:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 2px;
}
.x-transfer-list__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
}
```

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

使いどころ

  • 表に表示する列・チームに入れるメンバーのように、候補の中から「入れる側」と「入れない側」を分けて見せたいとき
  • 選んだ側の一覧そのものを見ながら確かめたいとき(何件選んだか、何が残っているか)

注意点

  • 候補が少ないならチェックボックスを並べるだけで足りる。左右移動リストは、選んだものを別の一覧として見せる価値があるときに使う
  • 複数選択プルダウンとの違いは、選んだ側がいつも見えていること。画面の幅を取るので、狭い画面では上下に積むか、別の部品にする
  • 一覧の見出しに件数を出し、移すたびに更新する。移したことが件数でも分かるようにする
  • 候補が多いときは各一覧の上に絞り込みの欄を置く。長い一覧をスクロールして探させない
  • ドラッグで移す操作だけにしない。ボタンで移せる道を必ず残す

アクセシビリティの要点

  • 各一覧は fieldset と legend(「表示しない列」)で名前を付け、項目はチェックボックスにする。選んだ状態がそのまま読み上げに乗る
  • 移すボタンには aria-label で「選んだ列を『表示する列』へ移す」のように行き先まで書く。矢印の絵だけでは向きしか伝わらない
  • 選択が無いときのボタンは disabled ではなく aria-disabled="true" にする。押した直後に disabled になると、そのボタンにあったフォーカスが失われる
  • 移したら role="status" の領域で「住所を表示する列へ移しました」と伝える(達成基準 4.1.3 ステータスメッセージ)
  • 選んだ行は塗りに加えてチェックの形と太字でも示す(達成基準 1.4.1 色の用途)

関連するパーツ