- 技術 💻
プルダウンとは|ドロップダウンとの違い・HTMLでの作り方6種比較
結論
プルダウンとは、ボタンや枠を押すと選択肢の一覧が下に開き、その中から選ぶ部品です。Web やアプリでは「ドロップダウン」とほぼ同じ意味で使われます。HTML で値を選ばせるなら、まず <select> を使います。
この記事でわかること
- プルダウンの場面別の意味と、ドロップダウンとの違い
- select・datalist・details・popover など6つの作り方の比較
- Chromium で測ったキーボード操作・送信値・読み上げ用の情報
「プルダウン」は、値を選ぶ入力欄(フォームの部品)にも、サイトのメニューにも使われる言葉です。HTML では、この2つは作り方が違います。最初に、何を作りたいかで部品を決めます。
flowchart TD
A[何をさせる?] -->|移動| N[開閉ボタン<br>とリンク]
A -->|値を選ぶ| B[一覧に無い値<br>も許す?]
B -->|許す| D[input と<br>datalist]
B -->|許さない| S[多ければ select<br>少なければラジオ]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| プルダウンとは | 押すと選択肢の一覧が開き、その中から選ぶ部品 | プルダウンとは(場面別の意味) |
| プルダウンとドロップダウンの違い | ほぼ同じ意味。厳密には「操作のメニュー」と「値を選ぶリスト」で分ける呼び方もある | プルダウンとドロップダウンの違い |
| プルダウンメニューの作り方(HTML) | <select> の中に <option> を並べる |
HTMLでプルダウンを作る |
| 初期値・「選ぶ」の案内を出したい | selected と、値が空の先頭の option |
HTMLでプルダウンを作る |
| 入力もできるプルダウン | <input list> と <datalist> |
入力もできるプルダウン |
| ナビゲーションのプルダウンメニュー | select ではなく、開閉ボタンとリンクの一覧で作る | プルダウンメニュー(ナビ)の作り方 |
| 連動プルダウン | 1つ目の change で2つ目の option を作り直す | 連動プルダウンをJavaScriptで作る |
| 見た目を変えたい | Chrome 135 以降は appearance: base-select |
プルダウンの見た目をCSSで変える |
| Excel・スプレッドシートのプルダウン | 別の記事で手順を扱っている | プルダウンとは(場面別の意味) |
確認環境:Ubuntu 24.04.5 LTS の上で、Python 3.13.18 の http.server を 127.0.0.1 のポートに起動し、select・select multiple・optgroup・datalist・details・開閉ボタン(aria-expanded)・popover・hover だけのメニュー・div で作った偽のプルダウン・連動プルダウン・カスタマイズできる select(appearance: base-select)を1枚のページに並べました。ブラウザは Chromium 153.0.8010.12(Playwright 1.63.0 の Python 版から操作、Linux 版・画面なしのモード)で、キーボード操作は Tab・矢印・Home・End・Enter・Space・Escape・Alt+↓・文字キーを押して value と開閉の状態を記録し、アクセシビリティツリーは Playwright の aria_snapshot と Chrome DevTools Protocol の Accessibility.getFullAXTree で取りました。フォームの送信は自分のサーバーに GET で送り、届いたクエリ文字列をそのまま記録しています。スマホは Playwright の iPhone 13 のエミュレーション(Chromium の描画)で測り、iOS・Android の実機の選択画面は見ていません。Windows・macOS での動きの違い、各要素の仕様とブラウザの対応は MDN、WAI-ARIA Authoring Practices Guide(APG)、WCAG 2.2、Chrome for Developers、Apple Human Interface Guidelines で確かめました。図は mermaid で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「プルダウンとは|ドロップダウンとの違い・HTMLでの作り方6種比較」
プルダウンとは(場面別の意味)
この節では、「プルダウン」という言葉が、使われる場面でどう意味が変わるかが分かります。
IT の画面でいうプルダウンは、押すと下に一覧が開く(引き下ろす=pull down)部品のことです。 英語の pull-down は「引き下ろす」の意味で、メニューを下に引き出すように開くことから来ています。ただし、同じカタカナが電子回路や筋力トレーニングでも使われるため、検索すると別の意味の記事が混ざります。
| 場面 | プルダウンの意味 | この記事での扱い |
|---|---|---|
| Web ページのフォーム | 選択肢から値を選ぶ入力欄。HTML の <select> |
中心に扱う |
| Web サイトのメニュー | 「サービス」などを押すと下にリンクの一覧が開くメニュー | 作り方を扱う |
| パソコンのアプリ(メニューバー) | 「ファイル」「編集」を押すと開く命令の一覧 | 違いの説明だけ |
| Excel・Google スプレッドシート | セルに付ける選択肢のリスト(入力規則・データの入力規則) | 手順は別記事 |
| 電子回路 | プルダウン抵抗。入力の電圧を低い側(0V)に寄せておく抵抗 | 扱わない |
| 筋力トレーニング | ラットプルダウン。バーを引き下ろす種目 | 扱わない |
プルダウンを使う理由は、どの場面でもほぼ同じです。選択肢を決めておくと、入力の手間が減り、表記の揺れ(「東京」「東京都」「とうきょう」)が無くなり、あとで集計しやすくなります。一覧は閉じていれば1行分の場所しか取らないため、選択肢が多くても画面を圧迫しません。その反面、開いてから選ぶ2段階の操作になり、閉じているあいだは選択肢が見えません。
Google スプレッドシートでのプルダウンの作り方(連動・色分け・複数選択)は、関連記事「スプレッドシートのプルダウンの作り方|連動・色分け・複数選択」で扱っています。この記事では、Web ページ(HTML)のプルダウンを扱います。
プルダウンとドロップダウンの違い
この節では、プルダウン・ドロップダウン・セレクトボックス・コンボボックスという似た言葉の関係が分かります。
日常の会話や仕様書では、プルダウンとドロップダウンは同じものを指すことがほとんどです。 区別するなら、「押すと命令(操作)の一覧が開くもの」をプルダウン、「値を1つ選んで、選んだ値が表示に残るもの」をドロップダウンリストと呼び分けます。Apple の Human Interface Guidelines は、この2つを別の部品として説明しています。
- Pull-down button:「ボタンの目的に直接関係する項目や操作のメニューを表示する」(Apple HIG)。例:「共有」を押すと「メール」「メッセージ」「コピー」が並ぶ。
- Pop-up button:「互いに排他的な選択肢のメニューを表示する」(Apple HIG)。例:並び順を「価格の安い順」「高い順」から1つ選ぶ。HTML の select はこちらに近い。
| 呼び方 | 主な意味 | HTML で近いもの |
|---|---|---|
| プルダウン(メニュー) | 押すと一覧が下に開く部品の総称。命令の一覧を指すこともある | select・開閉ボタン |
| ドロップダウン(リスト) | 値を1つ選ぶ一覧。選んだ値が表示に残る | <select> |
| セレクトボックス | HTML の select 要素の通称 | <select> |
| コンボボックス | 入力欄と候補の一覧を組み合わせた部品。入力できるものと、選ぶだけのものがある | <input list>・select |
| リストボックス | 開かずに、選択肢が最初から何行か見えている一覧 | <select multiple>・size |
読み上げソフトなどに伝わる「役割(ロール)」でも、この区別が出ます。手元の Chromium では、ふつうの select はコンボボックス(combobox)、multiple を付けた select はリストボックス(listbox)として伝わりました(「キーボード操作とアクセシビリティの比較」の節)。MDN も、multiple が無く size が1以下なら combobox、それ以外は listbox と書いています。
値を選ぶプルダウン(フォーム)
選んだ値がフォームで送られる
HTML の select・datalist で作る
役割は combobox か listbox
操作・移動のプルダウン(メニュー)
押すとページの移動や操作が起きる
button とリンクの一覧で作る
役割は button と link
HTMLのプルダウン6種類と使い分け
この節では、HTML でプルダウンのような見た目を作る6つの方法と、それぞれの向き不向きが分かります。
値を選ばせるなら select、自由入力に候補を出すなら datalist、ページへ移動するメニューなら開閉ボタン(または details・popover)です。 div などで一から作る方法は、キーボードと読み上げの対応を全部自分で書くことになります。手元で動かした結果をまとめます。
| 作り方 | 向いている用途 | Tab で届くか | 伝わる役割(Chromium) |
|---|---|---|---|
<select> |
決まった選択肢から1つ選ぶ | 届く | combobox |
<select multiple> |
複数を選ぶ(選択肢が多いとき) | 届く | listbox |
<input list>+<datalist> |
自由に入力でき、候補も出す | 届く | combobox |
<details>+<summary> |
開閉する簡単なメニュー・補足 | 届く(summary) | group と DisclosureTriangle |
| button(aria-expanded)+リンクの一覧 | サイトのナビゲーション | 届く | button(expanded)と link |
| button(popovertarget)+popover | 「その他」などの小さなメニュー | 届く | button |
| div・span で自作(悪い例) | 使わない | 届かなかった | ただの文字 |
select
選んだ値が必ず選択肢のどれかになる。キーボードと読み上げはブラウザが用意する。
迷ったらこれ
datalist
一覧に無い値も入れられる。候補は「おすすめ」で、制限ではない。
入力の補助
開閉ボタン
button の aria-expanded で開閉を伝え、中はリンクの一覧にする。
ナビゲーション向け
details・popover
JavaScript なしで開閉できる。popover は Escape と外側のクリックで閉じる。
小さなメニュー
HTMLでプルダウンを作る(selectとoption)
この節では、select の基本の書き方、初期値・案内の選択肢・グループ分け・選べない選択肢の書き方が分かります。
<select> の中に、選択肢の数だけ <option> を並べます。 value がサーバーに送られる値、タグの中の文字が画面に出る文字です。最後の北海道のように value を書かないと、中の文字がそのまま送られます。<label> の for と select の id をそろえると、項目名を押したときに select が選ばれ、読み上げでも項目名が伝わります。
<label for="pref">都道府県</label>
<select id="pref" name="pref" required>
<option value="">選んでください</option>
<option value="tokyo">東京都</option>
<option value="osaka">大阪府</option>
<option value="okinawa">沖縄県</option>
<option>北海道</option>
</select>
| やりたいこと | 書き方 | 手元で確かめた結果 |
|---|---|---|
| 最初に選ばれている値を決める | その option に selected |
新宿に selected を付けると、開いた直後の value が shinjuku |
| 何も付けないとき | 先頭の option が選ばれる(MDN) | 案内の選択肢(値が空)が選ばれていた |
| 選ばないと送れないようにする | select に required、先頭に値が空の option |
送信が止まり、メッセージが出た |
| 選択肢をグループに分ける | <optgroup label="…"> |
グループ名そのものは選べない |
| 一部の選択肢を選べなくする | option に disabled |
↓キーで飛ばされた |
| select ごと使えなくする | select に disabled |
値がフォームで送られなかった |
グループ分けと、選べない選択肢
<select id="station" name="station">
<optgroup label="山手線">
<option value="shibuya">渋谷</option>
<option value="shinjuku" selected>新宿</option>
</optgroup>
<optgroup label="中央線">
<option value="mitaka">三鷹</option>
<option value="tachikawa" disabled>立川(工事中)</option>
</optgroup>
</select>
新宿にいる状態で↓キーを3回押すと、三鷹で止まり、選べない立川には移りませんでした。
station ArrowDown -> mitaka
station ArrowDown -> mitaka
station ArrowDown -> mitaka
ただし、disabled は「使う人が選べない」だけで、JavaScript からは選べました。 select.value = 'b' で disabled の option を指定すると、value は b になりました。送られた値を信じず、サーバー側でも許された値かを確かめます。
required と案内の選択肢
MDN によると、select の最初の子の option で値が空のものは「プレースホルダーの選択肢」として扱われ、これが選ばれたままだと required の条件を満たしません。何も選ばずに送信ボタンを押すと、送信は止まり、サーバーには何も届きませんでした。
empty required validationMessage: Please select an item in the list. valid True
submitted so far []
valid True は validity.valueMissing が true(値が無い)という意味です。手元の Chromium は画面の言語が英語のため、メッセージも英語で出ています。multiple を付けた select や、size が2以上の select では、先頭の空の option はプレースホルダーとして扱われません(MDN)。
checkbox・radio と合わせた値の取り方は、関連記事「HTML select・checkbox・radioの書き方|初期値と値取得」で扱っています。
フォームで送られる値(実物)
同じフォームで値を選んで送り、自分のサーバーに届いたクエリ文字列です。
query1 pref=osaka&station=shinjuku&lang=py&lang=go&fw=Next.js&area=kansai&city=kyoto&color=red
query2 pref=%E5%8C%97%E6%B5%B7%E9%81%93&station=shinjuku&fw=&area=&color=red
| 見たところ | 送られた形 | 読み取れること |
|---|---|---|
| value のある option(大阪府) | pref=osaka |
画面の文字ではなく value が送られる |
| value の無い option(北海道) | pref=%E5%8C%97… |
文字の「北海道」が URL エンコードされて送られる |
| multiple で2つ選んだ | lang=py&lang=go |
同じ名前が2回並ぶ |
| multiple で何も選ばない | lang が無い | 空の値ではなく、名前ごと送られない |
| disabled の select(都市) | city が無い | 使えない select は送られない |
| datalist に無い値 | fw=Next.js |
候補に無い値でもそのまま送られる |
value を書かないと、表示の文字を変えただけで送られる値も変わります。 表示を「北海道」から「北海道(札幌)」に直すとサーバーの処理が合わなくなるため、value は必ず書きます。multiple の値をサーバーで受け取るときは、PHP なら name="lang[]" のように配列として受け取る書き方が要ります。
複数選択(multiple)
multiple を付けると、開閉するプルダウンではなく、選択肢が何行か見えているリストボックスになります。MDN によると、multiple を付けると size の既定は4になります。手元のキーボード操作の結果です(Linux の Chromium)。
focus lang selected: ''
multiple ArrowDown -> 'java'
multiple Shift+ArrowDown -> 'java,py'
multiple Shift+ArrowDown -> 'java,py,js'
multiple ArrowDown -> 'go'
multiple Control+ArrowDown -> 'go'
multiple -> 'go,rs'
click java -> java
ctrl+click go -> java,go
shift+click rust -> go,rs
- Shift+↓で続けて選び、Ctrl+↓で選んだまま移動して、Space で追加しました。 修飾キーを付けずに↓を押すと、それまでの選択が消えて1つだけになります。
- マウスでは、Ctrl(macOS は Command)+クリックで1つずつ追加、Shift+クリックで範囲の選択でした。MDN は、これらのキーの動きは標準化されておらず、ブラウザと OS で違うと書いています。
- 操作が分かりにくいため、選択肢が10個程度までならチェックボックスを並べるほうが迷いません。multiple を使うなら「Ctrl を押しながら選ぶと複数選べます」と案内を書きます(MDN も、複数選べることを伝えるよう書いています)。
入力もできるプルダウン(datalist)
この節では、入力欄に候補を出す datalist の作り方と、select との違いが分かります。
<input> の list と <datalist> の id をそろえると、入力欄に候補の一覧が出ます。 一覧に無い値も入力できるのが select との大きな違いです。
<label for="fw">フレームワーク</label>
<input id="fw" name="fw" list="fw-list" autocomplete="off">
<datalist id="fw-list">
<option value="React"></option>
<option value="Vue"></option>
<option value="Angular"></option>
<option value="Svelte"></option>
</datalist>
一覧に無い「Next.js」を入れて checkValidity() を呼ぶと true で、送信すると fw=Next.js がそのまま届きました。MDN も、datalist は「関連するコントロールに対するおすすめの値の一覧」で、一覧に無くても検証を通る値なら受け付けると書いています。
| select | input+datalist | |
|---|---|---|
| 一覧に無い値 | 入らない | 入る(候補は制限ではない) |
| 選択肢の絞り込み | 無い(文字キーで頭出し) | 入力した文字で候補が絞られる |
| 表示の文字と送る値を分ける | できる(value と中の文字) | 送るのは value。表示の扱いはブラウザで違う |
| 見た目の調整 | 限られる(base-select で広がった) | 一覧はほぼ変えられない |
| 注意点(MDN) | キーボード操作はブラウザと OS で違う | 一覧の文字は拡大されない。NVDA と Firefox では候補が読み上げられない |
MDN によると、option の label や中の文字は、Firefox では value の代わりに、Chrome と Safari では value に添えて表示されます。「候補から選ばせたいが、正しい値だけを受け付けたい」ときは datalist ではなく select にするか、サーバーで値を確かめます。
プルダウンメニュー(ナビ)の作り方:details・開閉ボタン・popover
この節では、サイトのナビゲーションのように「押すとリンクの一覧が開く」プルダウンメニューを、HTML でどう作るかが分かります。
ページへ移動するメニューを select で作らず、開閉ボタンとリンクの一覧で作ります。 select はフォームの値を選ぶ部品で、中の option はリンクになりません。WAI-ARIA の APG(作り方の手引き)は、サイトのナビゲーションの例で menu の役割(role="menu")を使わず、開閉(Disclosure)の型を使っています。理由として「一般的なサイトのナビゲーションには、menu と menubar の型で決められたキーボード操作の全部は要らない」と書いています。
開閉ボタン(aria-expanded)で作る
<nav aria-label="サイト">
<button id="disc-btn" aria-expanded="false" aria-controls="disc-menu">サービス</button>
<ul id="disc-menu" hidden>
<li><a href="/si">SI事業</a></li>
<li><a href="/ses">SES事業</a></li>
</ul>
</nav>
<script>
const btn = document.getElementById('disc-btn');
const menu = document.getElementById('disc-menu');
btn.addEventListener('click', () => {
const open = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!open));
menu.hidden = open;
});
document.addEventListener('keydown', (e) => { // Escape で閉じてボタンに戻る
if (e.key === 'Escape' && !menu.hidden) {
menu.hidden = true;
btn.setAttribute('aria-expanded', 'false');
btn.focus();
}
});
</script>
Enter -> aria-expanded true hidden False
Tab -> SI事業
Escape -> aria-expanded false focus disc-btn
Enter で開き、Tab で中のリンクへ進み、Escape で閉じてボタンに戻りました。APG も、Escape で閉じて、開いたボタンにフォーカスを戻す動きを書いています(WCAG 1.4.13 に関係)。
details と summary で作る(JavaScript なし)
<details>
<summary>メニュー</summary>
<ul>
<li><a href="/a">会社概要</a></li>
<li><a href="/b">事業内容</a></li>
</ul>
</details>
details Enter -> open = True
details Space -> open = False
details Space -> open = True
Escape -> details open True
outside click -> details open True
summary にフォーカスして Enter・Space で開閉しました。Escape では閉じず、外側を押しても閉じません。name 属性を同じ名前にそろえると、そのうち1つだけが開く(ほかが閉じる)ようになります(MDN)。開閉のアニメーションや name による排他は、関連記事「detailsアコーディオンをCSSのみで|開閉アニメーション・name排他」で扱っています。
popover で作る(Escape と外側のクリックで閉じる)
<button popovertarget="pm">その他</button>
<ul id="pm" popover>
<li><a href="/p1">設定</a></li>
<li><a href="/p2">ログアウト</a></li>
</ul>
Enter -> open True
Escape -> open False focus pm-btn
click -> open True / outside click -> open False
JavaScript を書かずに、Enter で開き、Escape で閉じてボタンにフォーカスが戻りました。開いたあとにページの外側を押しても閉じました。popover はページのいちばん上の層(top layer)に出るため、親の overflow: hidden で切れません。MDN では Popover API は「Baseline 2025」(2025年1月から主なブラウザの最新版で動く)です。ボタンの真下に置く位置合わせは CSS のアンカー配置(anchor-name)で書けます。手元の Chromium 153 では CSS.supports('anchor-name', '--a') が true でした。
hover だけで開くメニューの問題
CSS の :hover だけで開くメニューは、Tab で中のリンクに入れませんでした。:focus-within を足すと、Tab で中へ進めました。
.nav ul { display: none; }
.nav:hover ul,
.nav:focus-within ul { display: block; } /* focus-within を足すとキーボードでも開く */
hover-only: Tab from parent -> サービス(focus-withinあり) ← 中の項目1・2を飛ばした
focus-within visible True
focus-within: Tab from parent -> 項目A
MDN は、:hover はタッチスクリーンでは扱いが難しく、ブラウザによっては一度も当たらない、触れた一瞬だけ当たる、別の場所に触れるまで当たり続ける、のどれかになると書いています。スマホでも開けるように、押して開く button を用意します。
キーボード操作とアクセシビリティの比較
この節では、プルダウンの作り方によって、キーボードでの操作と、読み上げソフトに伝わる情報がどう違うかが、手元の実測で分かります。
閉じた select でのキー操作(Linux の Chromium)
ArrowDown -> tokyo / 東京都
ArrowDown -> osaka / 大阪府
End -> 北海道 / 北海道
Home -> / 選択してください
events ['pref:input:tokyo', 'pref:change:tokyo', 'pref:input:osaka', 'pref:change:osaka',
'pref:input:北海道', 'pref:change:北海道', 'pref:input:', 'pref:change:']
閉じたまま↓を押すと、一覧を開かずに値が変わり、そのたびに input と change が起きました。 そのため「change で別のページへ移動する」作り方にすると、キーボードで選ぼうとした人は、↓を1回押しただけで移動してしまいます。WCAG 2.2 の 3.2.2(入力時)は、部品の設定を変えただけで、前もって知らせずに状況(コンテキスト)が変わらないようにすることを求めており、別のページへの移動もこの「状況の変化」に入ります。移動には「移動」ボタンを別に置くか、リンクの一覧にします。
select で移動するメニュー
見分ける問い:「選んだだけで、ページや状況が変わるか」
開いた一覧でのキー操作と、文字キーでの頭出し
before Alt+Down :open False
after Alt+Down :open True
value while open JavaScript
after Enter value Python :open False
Space :open True
Esc :open False value Python
Alt+↓か Space で一覧が開き(:open が true)、開いているあいだの↓は強調が動くだけで value は変わらず、Enter で確定しました。Escape は value を変えずに閉じました。APG のコンボボックスの型でも、Alt+↓は「フォーカスを動かさずに一覧を出す」操作です。
type j -> Java
type j -> JavaScript
type j -> Java
type "py" -> Python
jp type r -> ''
jp type m -> ''
英字の選択肢は、文字キーで頭出しできました。同じ文字を続けて押すと Java と JavaScript を行き来し、「py」と続けて打つと Python に移りました。選択肢が「いちご・みかん・りんご」のようなひらがなのときは、r や m のキーでは選ばれませんでした。 日本語の選択肢が多いときは、五十音順に並べる、optgroup で分ける、datalist で絞り込めるようにする、などで探しやすくします(日本語入力ソフトを使った頭出しは手元では試していません)。
Tab の順番
pref -> station -> lang -> fw -> area -> cs -> send -> SUMMARY:メニュー(details -> disc-btn
-> A:サービス(hoverだけ -> A:サービス(focus-w -> A:項目A -> A:項目B -> pm-btn -> BODY
都市の select は disabled のため飛ばされ、div で作った偽のプルダウンには一度も止まりませんでした(tabIndex は -1)。
アクセシビリティツリー(読み上げソフトに伝わる情報)
-- select
- combobox "都道府県":
- option "選択してください" [selected]
- option "東京都"
-- select multiple
- listbox "言語(複数選択)":
- option "Java"
-- input + datalist
- combobox "フレームワーク(候補から選ぶか入力)"
-- details
- group: メニュー(details)
-- 開閉ボタン
- navigation "サイト":
- button "サービス"
-- div で作った偽のプルダウン
- text: 選択してください ▼
| 部品 | 役割(role) | 開閉の状態 | そのほか |
|---|---|---|---|
| select | combobox | expanded: false | hasPopup: menu。required で値が空なら invalid: true |
| select multiple | listbox | なし | option が並ぶ |
| input+datalist | combobox | ― | 候補は aria_snapshot に出なかった |
| details の summary | DisclosureTriangle | expanded: false | details 自身は group |
| 開閉ボタン | button | expanded: true(開いたとき) | aria-expanded がそのまま伝わる |
| div・span の自作 | なし(文字) | なし | 押せることも、開いたことも伝わらない |
div で作ったプルダウンは、画面では同じように見えても、読み上げソフトには案内の文字と「▼」がただの文字(text)としか伝わりませんでした。 自作するなら、role・aria-expanded・aria-activedescendant・キー操作(↑↓・Home・End・Enter・Escape・文字キー)を APG のコンボボックスの型に沿って全部書く必要があり、select より大きな手間になります。
スマホでのプルダウンの表示
この節では、スマホでプルダウンを使うときに気をつける大きさと、手元で測れた範囲・測れなかった範囲が分かります。
CSS を何も当てない select は、手元の Chromium で高さ19px、文字の大きさ13.33pxでした。Playwright の iPhone 13 のエミュレーション(幅390px・タッチあり)でも数字は同じで、エミュレーションは iOS の Safari の部品までは再現しません。
- WCAG 2.2 の 2.5.8(ターゲットのサイズ・最小)は、押す対象を 24×24 CSS ピクセル以上にすることを求めます。 ただし例外に「大きさがブラウザ(ユーザーエージェント)で決まり、作る人が変えていない」場合があり、CSS を当てない select はここに入ります。CSS で幅や高さ・余白を変えたら、24px 以上にそろえます。
- select の一覧そのものは、ブラウザと OS が描きます。スマホでは、ページの CSS で作った見た目とは別の、OS の選択画面で表示されます。手元では実機の iOS・Android を使っていないため、画面の形は書いていません。
- hover だけで開くメニューは、タッチでは開き方がブラウザで違います(MDN)。押して開く button にします。
- 選択肢が非常に多い(都道府県の47件を超えて、市区町村の数百件など)ときは、スマホで長い一覧を指で送ることになります。地方→都道府県→市区町村と連動プルダウンに分けるか、郵便番号からの入力にします。
連動プルダウンをJavaScriptで作る
この節では、1つ目のプルダウンで選んだ値に合わせて、2つ目の選択肢を入れ替える作り方と、つまずく点が分かります。
2つ目を disabled で置いておく
1つ目を選ぶまで2つ目を使えなくし、案内の option だけを入れておく。disabled の間は値が送られない。
選択肢をデータとして持つ
地方ごとの都市を、オブジェクト(または JSON)にまとめる。HTML に直接書かないと、増やすときに1か所で済む。
change で option を作り直す
replaceChildren で中身を消してから、new Option(表示, 値) で足す。前の地方の都市が残らない。
1つ目を戻したら2つ目も戻す
空の値に戻したら、2つ目を案内だけにして disabled に戻す。
<label for="area">地方</label>
<select id="area" name="area">
<option value="">地方を選ぶ</option>
<option value="kanto">関東</option>
<option value="kansai">関西</option>
</select>
<label for="city">都市</label>
<select id="city" name="city" disabled>
<option value="">先に地方を選ぶ</option>
</select>
<script>
const CITIES = {
kanto: [["tokyo", "東京"], ["yokohama", "横浜"], ["chiba", "千葉"]],
kansai: [["osaka", "大阪"], ["kyoto", "京都"]],
};
const area = document.getElementById('area');
const city = document.getElementById('city');
area.addEventListener('change', () => {
const list = CITIES[area.value] || [];
city.replaceChildren(new Option(list.length ? '都市を選ぶ' : '先に地方を選ぶ', ''));
for (const [value, text] of list) city.add(new Option(text, value));
city.disabled = list.length === 0;
});
</script>
city disabled True
area= kanto -> city options :都市を選ぶ, tokyo:東京, yokohama:横浜, chiba:千葉 disabled False
area= kansai -> city options :都市を選ぶ, osaka:大阪, kyoto:京都 disabled False
area= (空) -> city options :先に地方を選ぶ disabled True
JavaScript で値を入れると change は起きない
programmatic value set fires change?
log [] city options 1
after dispatch city options 4
set non-existing value -> value= '' selectedIndex -1
area.value = 'kanto'と書いて値を入れても change は起きず、都市は案内の1件のままでした。 保存した値を戻すとき(編集画面など)は、値を入れたあとにarea.dispatchEvent(new Event('change'))を呼ぶか、option を作る処理を関数にして直接呼びます。呼ぶと4件(案内+3都市)になりました。- 選択肢に無い値(
'kyoto'を都道府県に)を入れると、エラーは出ず、value が空・selectedIndex が -1(何も選ばれていない)になりました。
イベントの受け取り方とイベント委譲は、関連記事「addEventListenerの使い方|イベント一覧と解除・イベント委譲」で扱っています。
選択肢が多いときの作り方と速さ
選択肢を JavaScript で足す3つの書き方で、件数ごとに、作ってから画面の計算が終わるまでの時間を測りました(2 CPU のマシン、1回の値。マシンや混み具合で変わります)。
| 選択肢の数 | select.add(new Option) | DocumentFragment | innerHTML |
|---|---|---|---|
| 47件(都道府県) | 1.1ms | 1.1ms | 1.4ms |
| 100件 | 2.4ms | 1.8ms | 1.7ms |
| 1,000件 | 19.6ms | 17.6ms | 17.1ms |
| 10,000件 | 175.4ms | 239.4ms | 268.6ms |
1,000件までは、どの書き方でも 20ms 以内でした。 書き方の差より件数の差が大きく、10,000件では0.2秒前後かかりました。件数が数千を超えるときは、選ぶ側も一覧から探せないため、検索つきの入力(datalist やサーバーへの問い合わせ)に変えます。innerHTML に外から来た文字を入れると、HTML として解釈されるため、利用者の入力やAPIの値は new Option か textContent で入れます。
プルダウンの見た目をCSSで変える
この節では、select の見た目を変える2つの方法(以前からの方法と、appearance: base-select)が分かります。
以前からの select は、閉じた枠の幅・高さ・文字・枠線・背景は変えられますが、開いた一覧はほとんど変えられません。 MDN も、select は CSS で思いどおりに整えにくく、ブラウザをまたいでそろえるのが難しいと書いています。矢印を差し替えるときは appearance: none で既定の見た目を消し、背景画像などで矢印を描きます。
select {
appearance: none; /* 既定の矢印を消す */
min-height: 44px; /* 押しやすい高さ(24px 以上) */
padding: 0 2.5em 0 .75em;
font-size: 16px;
border: 1px solid #888;
border-radius: 6px;
background: #fff url("arrow.svg") no-repeat right .75em center / 1em;
}
select:focus-visible { outline: 3px solid #e88e2c; outline-offset: 2px; }
見た目を消したら、フォーカスの枠(:focus-visible)を必ず自分で描きます。消したままにすると、キーボードで今どこにいるか分からなくなります。
カスタマイズできる select(appearance: base-select)
Chrome 135 から、select と開いた一覧(::picker(select))の両方に appearance: base-select を指定すると、一覧の中まで CSS で整えられるようになりました(Chrome for Developers)。手元の Chromium 153 で CSS.supports('appearance', 'base-select') は true でした。
<style>
.cs, .cs::picker(select) { appearance: base-select; }
.cs option::checkmark { content: "✓"; }
.cs:open::picker-icon { rotate: 180deg; }
</style>
<label for="cs">色</label>
<select id="cs" name="color" class="cs">
<button><selectedcontent></selectedcontent></button>
<option value="red">赤</option>
<option value="green">緑</option>
<option value="blue">青</option>
</select>
#cs 53x31 font 16px appearance base-select
- combobox "色(カスタマイズできるselect)":
- button "赤"
- option "赤" [selected]
役割は以前の select と同じ combobox のままで、送信でも color=red が届きました。MDN によると、対応していないブラウザでは button と selectedcontent が無視され、option の中は文字だけになって、ふつうの select として動きます。対応していないブラウザでも使える「段階的な強化」として足せます。 ただし MDN は、まだ Baseline(主なブラウザすべてで動く状態)ではないこと、::checkmark と ::picker-icon の content は読み上げられないこと、一部の JavaScript フレームワークではサーバー側の描画(SSR)と組み合わせると hydration(画面の引き継ぎ)に失敗することがあると書いています。
input・checkbox と合わせたフォームの装飾は、関連記事「CSSフォームデザイン|input・select・チェックボックス装飾」で扱っています。
プルダウンがうまくいかないとき(症状→原因→直し方)
この節では、プルダウンを作ったときによく起きる症状と、その原因・直し方が、手元で出た原文とともに分かります。
| 症状 | 原因 | 直し方 |
|---|---|---|
NotAllowedError: Failed to execute 'showPicker' on 'HTMLSelectElement': showPicker() requires a user gesture. |
ページを開いたときなど、操作が無いのに一覧を開こうとした | ボタンのクリックなど、使う人の操作の中で showPicker() を呼ぶ |
Please select an item in the list. で送れない |
required で、値が空の案内の option が選ばれたまま | 案内のまま送れないのは仕様どおり。任意の項目なら required を外す |
| 2つ目のプルダウンが切り替わらない | JavaScript で value を入れても change は起きない | dispatchEvent(new Event('change')) か、作り直す関数を直接呼ぶ |
| value が空になり、何も選ばれない | 選択肢に無い値を入れた(selectedIndex が -1) | 入れる前に option があるか確かめる。保存した値の綴りを見直す |
| サーバーに値が届かない | select が disabled/multiple で何も選んでいない/name が無い | 使えないだけにしたいなら disabled の代わりに見た目と検証で止める。name を付ける |
| 送られる値が日本語の文字になる | option に value が無い | すべての option に value を書く |
| 複数選んだのに1つしか受け取れない | サーバーが同じ名前の2つ目以降を捨てている | 配列で受け取る(PHP は name="lang[]"、JavaScript は getAll) |
| ↓キーを押すと勝手にページが移動する | change で移動している | 移動はボタンかリンクの一覧にする |
| Tab でメニューの中に入れない | :hover だけで開いている/div で作った | :focus-within を足す。button とリンクで作り直す |
| 一覧の見た目が CSS で変わらない | 以前の select の一覧はブラウザと OS が描く | appearance: base-select(Chrome 135 以降)を足す |
| datalist で一覧に無い値が送られた | datalist の候補は制限ではない | select にするか、サーバーで許された値か確かめる |
showPicker() のエラーは、ページを開いたとき(load イベント)に呼んで出ました。ボタンのクリックの中から呼ぶ形なら、一覧を開けます。
プルダウンを使うべきでない場面(判断表)
この節では、プルダウンにしないほうが入力しやすい場面と、代わりに使う部品が分かります。
プルダウンは「選択肢が多く、1つを選ぶ」ときに向きます。 閉じていると選択肢が見えず、開いて選ぶ手間がかかるため、それ以外の場面では別の部品のほうが早く、間違いも減ります。
| 場面 | プルダウンにするか | 代わりに使うもの |
|---|---|---|
| 選択肢が2〜5個程度(性別・はい/いいえ・配送方法) | しない | ラジオボタン(全部が最初から見える) |
| オン・オフの1つだけ | しない | チェックボックス・スイッチ |
| 複数を選ぶ(10個程度まで) | しない | チェックボックス |
| 選択肢が多く、1つを選ぶ(都道府県) | する | select |
| 選択肢が数千件(駅名・商品名) | しない | input+datalist・検索つきの入力 |
| 生年月日・日付 | 年・月・日の3つは避ける | input type="date"・数字の入力欄 |
| 数量(1〜99) | しない | input type="number" |
| ほかのページへ移動する | select ではしない | 開閉ボタン・details・popover とリンク |
| 選択肢を比べながら選ぶ(料金プラン) | しない | カード型のラジオボタン・表 |
- 1label と select の id をそろえた項目名を押すと select が選ばれ、読み上げで項目名が伝わる
- 2すべての option に value を書いた表示の文字を直しても送る値が変わらない
- 3選んだだけでページが移動しない↓キー1回で change が起きる。移動はボタンかリンクで
- 4Tab と Escape で操作できるメニューは中のリンクに Tab で届き、Escape で閉じてボタンに戻る
- 5サーバーで値を確かめるdisabled の option も datalist に無い値も、送られてくることがある
- 1値を選ぶのか、移動・操作するのかを決める値なら select・datalist、移動ならボタンとリンク
- 2選択肢の数と、複数選べるかを見る少なければラジオ・チェックボックス
- 3一覧に無い値を許すかを決める許すなら datalist、許さないなら select
- 4見た目を変えるなら base-select を足すdiv で自作する前に
- 5キーボードとスマホで操作して確かめるTab・↓・Escape、押す対象の大きさ
1select は手間が少ない
キー操作・読み上げ・送信・検証をブラウザが用意している。手元でも combobox として伝わった。
2div の自作は伝わらない
Tab で届かず、読み上げには文字としか伝わらなかった。作るなら APG の型を全部書く。
3メニューはリンクの一覧
select で移動を作らない。開閉ボタンと aria-expanded、Escape で戻す。
よくある質問
Q. プルダウンとは何ですか? A. ボタンや枠を押すと選択肢の一覧が下に開き、その中から選ぶ部品です。Web のフォームでは HTML の select、サイトのメニューでは開閉するリンクの一覧を指すことが多いです。
Q. プルダウンとドロップダウンは違うものですか? A. ほとんどの場面で同じ意味です。区別するなら、命令や操作の一覧を開くものがプルダウン、値を1つ選んでその値が表示に残るものがドロップダウン(リスト)です。Apple の Human Interface Guidelines は pull-down button と pop-up button を別の部品にしています。
Q. HTML でプルダウンメニューを作るには?
A. フォームの値を選ばせるなら <select> の中に <option> を並べ、label を付けます。ページへ移動するメニューなら、button(aria-expanded)と ul・a のリンクの一覧、または details・popover で作ります。
Q. プルダウンの初期値を設定するには?
A. 最初に選ばせたい option に selected を付けます。何も付けないと先頭の option が選ばれます。JavaScript なら select.value = '値' ですが、このとき change は起きません。
Q. 「選んでください」を表示して、選ばないと送れないようにするには?
A. 先頭に value="" の option を置き、select に required を付けます。手元の Chromium では、案内のまま送信すると止まり、「Please select an item in the list.」(英語の画面のとき)が出ました。
Q. 入力もできるプルダウンは作れますか?
A. <input list="id"> と <datalist id="id"> で作れます。候補は「おすすめ」で、一覧に無い値も送られるため、決まった値だけを受け付けたいときは select にするか、サーバーで確かめます。
Q. 連動プルダウン(2段階のプルダウン)はどう作りますか? A. 1つ目の change イベントで、2つ目の option を消して作り直します。選択肢はオブジェクトか JSON にまとめておくと増やしやすくなります。保存した値を戻すときは、change が起きないので、作り直す処理を自分で呼びます。
Q. 選んだらすぐ別のページに移動するプルダウンは使ってよいですか? A. 避けます。手元の Chromium では、閉じた select で↓キーを押すたびに change が起き、キーボードで2つ目より下を選べなくなります。「移動」ボタンを別に置くか、リンクの一覧にします。
Q. プルダウンの見た目(矢印・一覧の色)を変えられますか?
A. 閉じた枠は appearance: none で既定の見た目を消して整えられます。開いた一覧の中まで変えるには、Chrome 135 以降の appearance: base-select を使います。対応していないブラウザでは、ふつうの select として動きます。
Q. div と JavaScript で自作したほうが自由ではありませんか? A. 見た目は自由になりますが、Tab で届く・役割を伝える・↑↓や Escape で操作する、を全部自分で作る必要があります。手元の div の例は、Tab で届かず、読み上げには文字としか伝わりませんでした。まず select と base-select で足りるかを確かめます。
Q. 複数選択できるプルダウンは作れますか?
A. select に multiple を付けると複数選べますが、開閉しない一覧になり、Ctrl や Shift を使う操作が分かりにくくなります。選択肢が少なければチェックボックスのほうが選びやすいです。
Q. Excel やスプレッドシートのプルダウンの作り方は? A. この記事は Web ページ(HTML)のプルダウンを扱っています。Google スプレッドシートのプルダウンは、関連記事で連動・色分け・複数選択まで扱っています。
まとめ
- プルダウンは、押すと選択肢の一覧が開く部品で、ドロップダウンとほぼ同じ意味。区別するなら「操作のメニュー」と「値を選ぶリスト」。電子回路や筋力トレーニングでは別の意味になる。
- 値を選ばせるなら select(option に必ず value、label を付ける)。一覧に無い値も許すなら datalist。どちらも、送られた値はサーバーで確かめる。
- ページへ移動するメニューは select で作らず、button(aria-expanded)とリンクの一覧、details、popover で作る。hover だけのメニューは Tab で中に入れなかった。
- 手元の Chromium では、閉じた select の↓キーで値と change が変わり、div で作ったプルダウンは Tab で届かず読み上げにも伝わらなかった。
- 連動プルダウンは change で option を作り直す。JavaScript で value を入れても change は起きない。見た目は Chrome 135 以降の appearance: base-select で一覧の中まで変えられる。
参考・出典
確認日はいずれも 2026年10月29日です。
- MDN「<select>: The HTML Select element」:https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/select
- MDN「<datalist>: The HTML Data List element」:https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/datalist
- MDN「<details>: The Details disclosure element」:https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details
- MDN「Customizable select elements」:https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select
- MDN「Popover API」:https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
- MDN「:hover」:https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:hover
- Chrome for Developers「The <select> element can now be customized with CSS」:https://developer.chrome.com/blog/a-customizable-select
- W3C WAI-ARIA Authoring Practices Guide「Example Disclosure Navigation Menu」:https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/
- W3C WAI-ARIA Authoring Practices Guide「Combobox Pattern」:https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
- W3C「Understanding Success Criterion 3.2.2: On Input」:https://www.w3.org/WAI/WCAG22/Understanding/on-input.html
- W3C「Understanding Success Criterion 2.5.8: Target Size (Minimum)」:https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html
- Apple Human Interface Guidelines「Pull-down buttons」:https://developer.apple.com/design/human-interface-guidelines/pull-down-buttons
- Apple Human Interface Guidelines「Pop-up buttons」:https://developer.apple.com/design/human-interface-guidelines/pop-up-buttons
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「プルダウンとは|ドロップダウンとの違い・HTMLでの作り方6種比較」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。