select を JS と CSS で独自にデザインする方法(幅可変版)

ブラウザデフォルトの select 要素は、いろいろな事情で JavaScript でゼロから実装しないといけない場合はあるにせよ、特に理由がなければそのまま使うことが望ましい。独自に実装したものはどうしても、デフォルトのものよりユーザビリティーやアクセシビリティーの面で劣ってしまう。

とはいえそのまんま使うとデザインにそぐわない場面は少なくないし、デザイナーがゆるさんという場合だってある。見た目を変更しなきゃいけない時はよくある。

UA が提供する見た目があまりにもデザインとマッチしなければ select の見た目を CSS を使って調整することになる。このやり方は最近では一般的になってきていて、Google などでも使われている(路線検索時)など。やり方をググればブログなどがいくつか引っかかる程度にはメジャーだ。が、これらは全部、幅を固定しなければいけないという制約がある。

そういうわけで、幅を可変にしつつ、select の見た目を独自にデザインする方法を探った。

成果物はこちら。

特徴

  • 幅を固定する必要がない
  • テキストと並べて置ける
  • キーボードフォーカスが当てられる
  • キーボードの上下で移動ができる
  • 候補の最初の文字をタイプすることで頭出しができる
  • max-width を設定できる

ちょっと解説

可変幅はどうやって実現しているかというと、まず <select> には width を指定していない。値を反映する .select-value には position: absolute しつつ上下左右(top, right, bottom, left)を 0 とすることで <select> と同じ大きさになるよう制御している。

.select-selectpadding-right などがマジックナンバー的になっているのがちょっと心残り。この値は本来、 <select> がもともと持っている下向き矢印の箱の大きさを加味せねばならず、この箱の大きさは OS や UA、画面の解像度などで変わってくるため完璧に安全な数値が出せないっぽい。appearance: none; も試したけれど、 Firefox で下向き矢印が消えないのでダメだった。

JavaScript がやっていることは、選択内容を変更を監視して表示に反映しているだけになっている。JavaScript の介入を極力控えめにすることでユーザビリティ、アクセシビリティが損なわれるのを防いでいる。そのためキーボードフォーカス、キーによる上下移動、候補の頭出しなどができる。

change イベント以外に keydown イベントも監視している。これは Firefox でキーの上下操作をしても change イベントが発火されないという仕様による。Firefox が悪いというわけではないみたい。keyup イベントでもよかったのだけど、画面への反映が遅れるのがいやだったので、keydown イベントを監視しつつ、setTimeout を使って一瞬遅らせることで表示の反映をした。

対応ブラウザ

  • IE 8以上
  • その他の今どきのブラウザ