Webパーツ辞典

連動プルダウンCascading select

1つ目のプルダウンで選んだ値に応じて、2つ目の選択肢が入れ替わる組。都道府県を選ぶと市区町村が選べるようになる形が代表で、2つ目は1つ目を選ぶまで押せない。

表示例

東京都の市区町村に切り替えました

別名・英語表記

  • カスケード選択
  • 連動セレクト
  • 階層プルダウン
  • 従属プルダウン
  • カスケーダー
  • 都道府県と市区町村の選択
  • Cascading select

実装例

<div class="x-cascader">
  <div class="x-cascader__f">
    <label class="x-cascader__l" for="x-cascader-pref">都道府県</label>
    <select class="x-cascader__s" id="x-cascader-pref" name="pref">
      <option value="">選択してください</option>
      <option value="13" selected>東京都</option>
      <option value="14">神奈川県</option>
    </select>
  </div>
  <div class="x-cascader__f">
    <label class="x-cascader__l" for="x-cascader-city">市区町村</label>
    <!-- 都道府県を選ぶまでは disabled にして、選択肢も空にしておく。
         選び直したら値を空に戻し、選択肢を入れ替える -->
    <select class="x-cascader__s" id="x-cascader-city" name="city" aria-describedby="x-cascader-n">
      <option value="">選択してください</option>
      <option value="13101">千代田区</option>
      <option value="13102">中央区</option>
      <option value="13103">港区</option>
    </select>
  </div>
  <!-- 選択肢を入れ替えたらここを書き換えて、控えめに伝える -->
  <p class="x-cascader__n" id="x-cascader-n" role="status">東京都の市区町村に切り替えました</p>
</div>

AIへの指示文

「連動プルダウン(Cascading select)」を実装してください。

## これは何か
1つ目のプルダウンで選んだ値に応じて、2つ目の選択肢が入れ替わる組。都道府県を選ぶと市区町村が選べるようになる形が代表で、2つ目は1つ目を選ぶまで押せない。

## 使いどころ
- 都道府県 → 市区町村、大分類 → 小分類、メーカー → 車種のように、選択肢が上の段の値で決まるとき
- 全部を1つのプルダウンに並べると数百件になり、探せなくなるとき

## 実装の要件
- label + select(1段目)→ label + select(2段目・初期は disabled で選択肢は「選択してください」だけ)+ role="status" の文
- 1段目の change で2段目の option を作り直し、値を空に戻して disabled を外す
- 選択肢のデータは { "13": [["13101", "千代田区"], …] } のように1段目の値をキーにして持つ
- 1つ目を選び直したら、2つ目の値を空に戻す。前の県の市区町村が選ばれたまま送信される不具合が起きやすい
- 2つ目の選択肢をサーバーから取るなら、読み込み中を見せる。その間は2つ目を disabled にしておく
- 選んだ瞬間に次の欄へフォーカスを移したり、送信したりしない。プルダウンを矢印キーで動かしているだけで値が変わる環境がある(WCAG 3.2.2)
- 住所なら郵便番号からの自動入力のほうが速いことが多い。段が3段以上になるなら、オートコンプリートで一度に探せないか考える
- Ant Design などの「Cascader」は、1つのボタンから開いたパネルに列を並べて選ぶ別の形。ここで扱う「プルダウンを並べる形」より作り込みが要る

## アクセシビリティの要件
- それぞれの select に label を for / id で結ぶ
- 2つ目を disabled にしている間は、なぜ選べないか(「先に都道府県を選んでください」)を文字で出す。disabled の select は Tab で飛ばされるので、Tab だけで進む人はそこに欄があることに気づきにくい
- 選択肢を入れ替えたら role="status" の領域で「東京都の市区町村に切り替えました」と控えめに伝える
- disabled の見た目は薄くするだけにせず、地の色や枠線の形も変える

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

```html
<div class="x-cascader">
  <div class="x-cascader__f">
    <label class="x-cascader__l" for="x-cascader-pref">都道府県</label>
    <select class="x-cascader__s" id="x-cascader-pref" name="pref">
      <option value="">選択してください</option>
      <option value="13" selected>東京都</option>
      <option value="14">神奈川県</option>
    </select>
  </div>
  <div class="x-cascader__f">
    <label class="x-cascader__l" for="x-cascader-city">市区町村</label>
    <!-- 都道府県を選ぶまでは disabled にして、選択肢も空にしておく。
         選び直したら値を空に戻し、選択肢を入れ替える -->
    <select class="x-cascader__s" id="x-cascader-city" name="city" aria-describedby="x-cascader-n">
      <option value="">選択してください</option>
      <option value="13101">千代田区</option>
      <option value="13102">中央区</option>
      <option value="13103">港区</option>
    </select>
  </div>
  <!-- 選択肢を入れ替えたらここを書き換えて、控えめに伝える -->
  <p class="x-cascader__n" id="x-cascader-n" role="status">東京都の市区町村に切り替えました</p>
</div>
```

```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-cascader,
.x-cascader *:not([class*="fa-"]),
.x-cascader *:not([class*="fa-"])::before,
.x-cascader *: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-cascader {
  width: 320px;
  max-width: 100%;  /* 狭い画面では親の幅に合わせて縮む */
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  color: #1b2028;
}
.x-cascader__f {
  flex: 1 1 0;
  min-width: 0;
}
.x-cascader__l {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  font-weight: 700;
}
.x-cascader__s {
  width: 100%;
  height: 44px;
  padding: 0 8px;
  border: 1px solid #7f8a99;
  border-radius: 8px;
  background: #fff;
  color: #1b2028;
  font-size: 14px;
}
/* 選べない間は、色を薄くするだけでなく地の色と枠の線も変える */
.x-cascader__s:disabled {
  border-style: dashed;
  background: #f1f3f6;
  color: #5b6472;
  cursor: not-allowed;
}
.x-cascader__s:focus-visible {
  outline: 2px solid #1d4ed8;
  outline-offset: 1px;
}
.x-cascader__n {
  flex-basis: 100%;
  margin: 0;
  color: #5b6472;
  font-size: 11px;
}
```

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

使いどころ

  • 都道府県 → 市区町村、大分類 → 小分類、メーカー → 車種のように、選択肢が上の段の値で決まるとき
  • 全部を1つのプルダウンに並べると数百件になり、探せなくなるとき

注意点

  • 1つ目を選び直したら、2つ目の値を空に戻す。前の県の市区町村が選ばれたまま送信される不具合が起きやすい
  • 2つ目の選択肢をサーバーから取るなら、読み込み中を見せる。その間は2つ目を disabled にしておく
  • 選んだ瞬間に次の欄へフォーカスを移したり、送信したりしない。プルダウンを矢印キーで動かしているだけで値が変わる環境がある(WCAG 3.2.2)
  • 住所なら郵便番号からの自動入力のほうが速いことが多い。段が3段以上になるなら、オートコンプリートで一度に探せないか考える
  • Ant Design などの「Cascader」は、1つのボタンから開いたパネルに列を並べて選ぶ別の形。ここで扱う「プルダウンを並べる形」より作り込みが要る

アクセシビリティの要点

  • それぞれの select に label を for / id で結ぶ
  • 2つ目を disabled にしている間は、なぜ選べないか(「先に都道府県を選んでください」)を文字で出す。disabled の select は Tab で飛ばされるので、Tab だけで進む人はそこに欄があることに気づきにくい
  • 選択肢を入れ替えたら role="status" の領域で「東京都の市区町村に切り替えました」と控えめに伝える
  • disabled の見た目は薄くするだけにせず、地の色や枠線の形も変える

作るときに使えるツール

関連するパーツ