Choices.jsをカスタマイズし、2行のセレクトボックスを作成する！


この記事では、ありそうでなかった？2行のセレクトボックスを作る方法についてご紹介します。

完成例がコチラ👇
<p class="codepen" data-height="590" data-theme-id="light" data-default-tab="html,result" data-user="tuubo" data-slug-hash="eYNLvab" style="height: 590px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="CustomSelect">
  <span>See the Pen <a href="https://codepen.io/tuubo/pen/eYNLvab">
  CustomSelect</a> by tuubo (<a href="https://codepen.io/tuubo">@tuubo</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

こういうの作りたかった！と思った方は、下の説明も読んでより自分好みのセレクトボックスを作る参考にしてみてください。



それでは解説！
## Choices.jsを読み込む

タイトルにありますが、ここでは[Choices.js](https://github.com/jshjohnson/Choices)というライブラリを使用しています。
ライブラリを使用することで、より簡単に高機能で自由なセレクトボックスを作成することができます。
中規模な開発ならnpmやyarn、ちょっとお試しの場合はCDNから、自分の好きな方法でライブラリを読みこんでください。

npmの場合

```
npm install choices.js
```

yarnの場合

```
yarn add choices.js
```

CDNから読み込む場合

```html
<head>
  <!-- タイトルなど -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js/public/assets/styles/choices.min.css">
</head>
<body>
  <!-- コード -->
  <script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.min.js"></script>
</body>
```


## カスタムテンプレートを作成する
今回の肝ですね！
Choicesのセレクトボックスをカスタマイズする際は　[callbackoncreatetemplates](https://github.com/jshjohnson/Choices#callbackoncreatetemplates)　の記述を変更していきます。

```.js
const example = new Choices(element, {
  callbackOnCreateTemplates: function(template) {
    return {
      item: (classNames, data) => {
        // ここを変更する
      },
      choice: (classNames, data) => {
        // ここを変更する
      },
    };
  },
});
```
公式のGitHubでItem, Choiceは次のように定義されています。

| 用語 | 定義 |
|:--:|:--|
| Choice | ユーザーが選択可能な値。select要素内の`<option></option>`に相当する。 |
| Item | 入力された値(text input)、または選択された値(select要素)。セレクト要素では`<option value="Hello" selected></option>`に、input要素では`<input type="text" value="Hello">`に相当する。 |

つまり`<option></option>`をカスタマイズしたい場合は`choice`を、常に表示されている`<option value="XXX" selected></option>`の部分をカスタマイズしたい場合は`item`を変更していきます。

今回の例ではitemとchoiceを同じように書き換えているのですが、itemの方をもう少し詳しく見ていきましょう。

```.js
item: (classNames, data) => {
          return template(`
          <div class="
            ${classNames.item}
            ${data.highlighted
              ? classNames.highlightedState
              : classNames.itemSelectable
            }
            ${data.placeholder ? classNames.placeholder : ''}
            custom-select //クラスを追加
          "
          data-item data-id="${data.id}" data-value="${data.value}"
          ${data.active ? 'aria-selected="true"' : ''}
          ${data.disabled ? 'aria-disabled="true"' : ''}
          >
            <span>No. ${data.value}</span> //追加
            <span>${data.label}</span>     //追加
          </div>
          `);
        },
```


コードが煩雑で難しく見えるかと思いますが、少し省略すると`return template(``);`の中は、このような形になっています。


```.html
<div class="..." data-item data-id="${data.id}" data-value="${data.value}">
  <span>No. ${data.value}</span>
  <span>${data.label}</span>
</div>
```
一気に見やすくなったのではないでしょうか。
classの中身や、data属性の切り替えに三項演算子が多用されているので複雑なコードに見えますが、よく切り分けてみると非常にシンプルなコードなのです。


実際書き加えた部分は、見た目をカスタマイズするためのクラスを`class="..."`内に1つと、
2行のセレクトボックスを作成するための、2つの`<span>`要素だけです。


カスタマイズしたセレクトボックスを機能させるためには、以下のようなclass内の記述や様々なデータ属性が必要となります。
しかし「そういうもの」として記述を残してさえおけば、他の大規模な書き換えは必要ないのです。

```.js
<div class="
    ${classNames.item}
    ${data.highlighted
      ? classNames.highlightedState
      : classNames.itemSelectable
    }
    ${data.placeholder ? classNames.placeholder : ''}
  "
  data-item data-id="${data.id}" data-value="${data.value}"
  ${data.active ? 'aria-selected="true"' : ''}
  ${data.disabled ? 'aria-disabled="true"' : ''}
>
```


## 2つの値をセレクトボックスに表示させる
次のコードを見てください。

```.html
<select class="js-choice" name="bank" id="bank">
  <option value="0001">みずほ銀行</option>
  <option value="0005">三菱UFJ銀行</option>
  <option value="0009">三井住友銀行</option>
  <option value="0010">りそな銀行</option>
</select>
```
Choices.jsではデフォルトの設定で、`<option></option>`の中に書いた文字列と`value=""`の中の値をそれぞれ`data.label`と`data.value`として読み取ってくれます。
これを利用すると以下のように、

```.html
  <span>No. ${data.value}</span>
  <span>${data.label}</span>
```
`${data.value}`や`${data.label}`をカスタムテンプレート内に書くことで、optionに設定した二つの値を表示させることが可能です。

注意が必要なのは、`<option>`に設定した値すべてを読み込んでくれるわけではないということです。
例えば、`<option value="aaa" data-code="0900">XXX</option>`とhtmlに書き、`value`の値と`data-code`の値の二つをセレクト内に表示させようとしても、`data-code`の値はdataの中に保存されていないので、読み込むことができないのです。

様々な記述を加えれば、`value`とラベル以外の値も扱えるようにはなるのですが、とりあえずは、一番簡単に利用できるのは`value`と`ラベル`の2つだけと覚えてもらえればいいかと思います。

## 見た目を整える。
先ほど、見た目のカスタマイズ用に設定した`.custom-select`に次のようなcssを用意することで、今回の目標だった2行のセレクトボックスを実現します。

```.css
.custom-select {
  display: flex;
  flex-direction: column;
}
```

ここではflexを用いて、縦並びを実現させるという形をとしましたが、span要素を`display: block;`に変えることでも、2行のセレクトボックスは実現できたかなと思います。


## その他の設定
今回はカスタマイズ以外はほとんどデフォルトの設定のままですが、次の設定だけは変更しています。

```.js
shouldSort: false,
```

デフォルトの設定ではラベルの部分を辞書順に並び替えて表示されるのですが、今回はhtmlに書いた順で表示をさせたかったため、この設定をfalseにしています。


長くなりましたが、以上で解説を終わります！
Choices.jsのカスタマイズ方法、参考になったでしょうか？