レガシーなウェブシステムのSELECTタグを使いやすくするJavascript(ブックマークレット)

久しぶりにブックマークレット

# 困りごと
うちの会社には、スーパーなレガシーシステムがありまして、、、
HTMLで作られているWebシステムなのですが、`<select>`がめっちゃ長いんですよ。選択肢が50とか100とかあります。資格の名前とか事業所の一覧とか、似たような名前が多くて地獄です。

画面で入力する身としては、たまったもんじゃありません。目で見て選択するなんて無理です。

ということで、レガシーシステムに息を吹き込む**ブックマークレット**を作りました。

# 使うもの

- Javascript
- jQuery

レガシーシステムが相手なので、jQueryで楽させてもらいます。

# コード
こんな感じになりました。コメント入れて60行くらい。
さすがにレガシーといえど、jQueryを使ってコード書きました。

```javascript:select-filter.js
void ((function (f) {
  if (window.jQuery && jQuery().jquery > '2.0') {
    console.log('use jquery');
    f(jQuery);
  } else {
    console.log('load jquery');
    var script = document.createElement('script');
    script.src = '//code.jquery.com/jquery-3.5.1.slim.min.js';
    script.onload = function () {
      var $ = jQuery.noConflict(true);
      f($);
    };
    document.body.appendChild(script);
  }
})(
  function ($, undefined) {
    $('select').each(function (i, select) {
      //selectにidが無ければ、nameから作り出す
      if (!select.id) {
        select.id = select.name + '_' + i;
      }

      let options = $(select).children('option').length;
      let text = `<input type="text" data-selectid="${select.id}" class="__ft">`;
      let badge = `<span id="${select.id}_hit">${options}</span>/<span>${options}</span>`;

      //textboxを突っ込む
      $(select).before(`${text}  ${badge} <br>`);
    });

    //追加したテキストボックス全てに適用するイベントハンドラ
    $('input.__ft').on('input', function (ev) {

      let selector = '#' + $(this).data('selectid');
      let selectid = $(selector).attr('id');
      console.log(selector);

      let txt = this.value;
      console.log(txt);

      //フィルタした瞬間に選択肢を元に戻すならコメントはずす
      // $(selector).prop("selectedIndex", 0);

      let hit = 0;
      $(selector).children('option').each(function (i, e) {
        if (isDefaultOption(e) || matchOption(e, txt)) {
          $(e).show();
          hit++;
        } else {
          $(e).hide();
        }
      });
      $(`span#${selectid}_hit`).text(hit);
    });

    //デフォルトオプションの判定（システムに合わせて適当に書き換える必要があるかも）
    function isDefaultOption(element) {
      return $(element).val() == '' || $(element).hasClass('default');
    }
    //表示する条件(半角全角の調整など必要なことをやるべし)
    function matchOption(element, input) {
      return $(element).text().indexOf(input) != -1;
    }
  }
))

```

コードはここにも置いてあります。
https://github.com/kanaxx/kanaxx.github.io/tree/master/bookmarklet/select-filter

## コードの説明
:sunflower: 説明するほどのコードじゃないですけど、一応。

`$('select')`なので、画面にある全ての\<select>が対象です。

\<select>に`id`が振られていないようなレガシーシステムの場合のことも考えて、`name`の値を元に\<select>に`id`を追加しています。

テキストボックスの真下にある\<select>の`id`をテキストボックスの`data-selectid`として仕込むことで関連を持たせ、`class`に`__ft`クラスを割り当てる

`__ft`クラス全体にinputイベントハンドラを登録する

イベントハンドラでselectからoptionを探して、部分一致したものだけ`show()`する

## 実行
選択肢が多いページが必要だったので、自分で作りました。
https://kanaxx.github.io/bookmarklet/select-filter/index.html

F12でDevToolを開いて、Consoleにスクリプトを投げ込んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/4bc93b0b-2fb6-70b2-c42c-4442e838df48.png)


**初期表示はセレクトがあるだけ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/675e3f86-59ed-bd7c-a267-8363215e3c2d.png)

長くてうんざり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/865b77ea-8aa1-ce2e-3e4f-fc88dfd07fa0.png)

**ブックマークレット（スクリプト）を実行**
画面内にあるセレクトの上に、テキストボックスが生まれる！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/6c5f7442-140d-818e-ee3b-de82718b2bca.png)


**フィルタ実行**
テキストに「都」を入れると「東京都」と「京都府」で2つの選択肢だけになるので、選びやすい。3/48表記は左がマッチした選択肢数、右が選択肢の総数です。テキストボックスから消すと全部元通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168315/a780ea50-95cf-348b-4ced-44f1cdcb966f.png)

# まとめ
程よく汎用的に作ったので、たいていのサイトの選択肢\<select>をハックできるはずです。**一番上の選択肢**を上手に判定する方法が見つからなかったけど。
気に入ったら、ブラウザのブックマークバーに登録してみてください。

## 参考資料
国の一覧はここを一覧を使わせていただきました。5年前の国名らしいので2020年だと変わってるかもしれません。
https://qiita.com/tao_s/items/32b90a2751bfbdd585ea