2022年10月21日金曜日

JavaScript で autocomplete をいい感じに作る

JavaScript で autocomplete をいい感じに作る方法をまとめました。 今回ターゲットにしているのは、文字列配列を、前方一致検索で、手軽に、いい感じに autocomplete する方法です。 最近の HTML/JavaScript は進化しているので、autocomplete / suggest / incremental search と呼ばれる類の UI を作ることは簡単になっています。 反面、ベストプラクティスはわかりにくくなっているのでメモっておきます。

1. HTML の標準機能を使う→駄目

まず最近私がやっていたのは、HTML の input をそのまま使って、list 属性に datalist の ID を指定する方法です。 MDN に良いサンプルがあるのでそのまま引用します。 ライブラリを使わないでも作れるので手軽で、動作も非常に軽快です。
<label for="ice-cream-choice">Choose a flavor:</label>
<input list="ice-cream-flavors" id="ice-cream-choice" name="ice-cream-choice">

<datalist id="ice-cream-flavors">
    <option value="Chocolate">
    <option value="Coconut">
    <option value="Mint">
    <option value="Strawberry">
    <option value="Vanilla">
</datalist>


もうこれでいいじゃんと思っていた時期もあったのですが、データ数が 3000 を超えると PageSpeed Insights が、 「過大な DOM サイズの回避をしてください」と怒ってきます。 この仕様は不満ですが仕方ない。データ数が増えたら DOM に依存しすぎないライブラリを使うべきのようです。

2. 高速なライブラリを使う→意外とない

autocomplete / suggest / incremental search といったキーワードでライブラリを検索すると、たくさんのライブラリが見つかります。 だいたいは autocomplete で探すと良いですが、index search なども対象に入れると良いかもしれません。 見つかった色々なライブラリを試してみたのですが、多くのライブラリはデータ数が 1万を超えると壊滅的に遅くなってしまう問題があります。 有名なものも、古めのライブラリはほぼアウトです。 高速なライブラリが求められます。 速度に関して今のところ最も参考になるのは、最近登場した uFuzzy の benchmark ですが、それほど多くはまとまっていません。 また fuzzy search なので参考程度です。だいたい実装してみるしかありません。

3. UI が使いやすくカスタマイズ性の高いライブラリを使う

そしていざ autocomplete を実装しようとすると速度だけでなく UI を作るほうが面倒なことにも気付きます。 高速で簡単に UI を作ってくれるライブラリが欲しいです。 改めてライブラリを探してみると、まずはJSON で構造化されたデータから検索したいなら minisearch などがわかりやすいと思いました。 しかしただの文字列配列には対応していないように見えます。また同じような条件のライブラリが多く、意外と良いライブラリが見つからなくて困りました。 autocomplete のライブラリは UI が特殊なライブラリも多かったのですが、昨今の CSS Framework 事情的には、選択候補だけ表示してくれるようなものが良いと思います。 たとえばこのへんのライブラリをチェックしていました。
最終的に一番良いなと思ったのが、kraaden/autocomplete です。 基本は JSON 形式へのデータを対象としていますが、ただの文字列配列にも適用でき、処理をフルカスタマイズでき、高速に動作します。

実装例

実装例もまとめておきます。複雑な設定がないので凄くわかりやすい。 このコードを見ればだいたい何にでも対応できそうなことがわかると思います。
autocomplete({
  input: input,
  fetch: function (text, update) {
    const suggestions = tags.filter((tag) => tag.startsWith(text));
    update(suggestions);
  },
  render: function (item) {
    const itemElement = document.createElement("div");
    itemElement.textContent = item;
    return itemElement;
  },
  onSelect: function (item) {
    input.value = item;
    searchIcons();
  },
});
kraaden/autocompleteIcon Search で利用しています。

追記: 試してないけど、trevoreyre/autocompletealgolia/autocomplete も良さそう。

0 件のコメント: