JavaScriptのオートコンプリートライブラリautoComplete.jsを使ってみる

## autoComplete.jsとは

レファレンスによると、以下の特徴があります。

* Vanilla.js
* 他のライブラリを必要としない
* シンプルで簡単
* 軽い
* 速い
* 汎用性が高い
* カスタマイズ性が高い

> 公式レファレンス
>
> https://tarekraafat.github.io/autoComplete.js/#/?id=api-configuration



## ハンズオン

### 1. 環境構築

autoComplete.jsでは直接DOMを触るのでReactを使う恩恵はありませんが、環境構築が楽なので`create-react-app`で動かしてみましょう。Yarnの環境が整っていない方は、Yarnのインストールもしておいてください。

```sh
$ yarn create react-app autocomplete-react
$ yarn add @tarekraafat/autocomplete.js
```

### 2. CSSの読み込み

`public/index.html`のheadに以下を追加します。

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@7.2.0/dist/css/autoComplete.min.css">
```

### 3. App.jsの書き換え

`src/App.js`を以下のように書き換えます。

```react
import React, { useEffect } from 'react';
import autoComplete from '@tarekraafat/autocomplete.js'

const App = () => {
  useEffect(() => createAutoComplete())
  return (
    <>
      <input id="autoComplete" type="text" tabIndex={1}></input>
      <p className="selection"></p>
    </>
  )
}

const createAutoComplete = () => {
  const autoCompletejs = new autoComplete({
    // データ
    data: {
      src: fetch('https://tarekraafat.github.io/autoComplete.js/demo/db/generic.json').then(h=>h.json()),
      // src内で
      key: ["food", "cities", "animals"],
      cache: true
    },
    // 結果のソート
    sort: (a, b) => {
      if (a.match < b.match) return -1;
      if (a.match > b.match) return 1;
      return 0;
    },
    // inputの初期値
    placeHolder: "Food & Drinks",
    // inputのセレクタ
    selector: "#autoComplete",
    // 実行される最低文字数
    threshold: 0,
    // 遅延
    debounce: 0,
    // 検索モード
    searchEngine: "strict",
    // ハイライト
    highlight: true,
    // 最大表示数
    maxResults: 5,
    // 結果表示の詳細
    resultsList: {
      render: true,
      container: source => {
        source.setAttribute("id", "autoComplete_list");
      },
      destination: document.querySelector("#autoComplete"),
      position: "afterend",
      element: "ul"
    },
    // 結果表示の加工
    resultItem: {
      content: (data, source) => {
        source.innerHTML = data.match;
      },
      element: "li"
    },
    // マッチする結果が存在しなかった場合
    noResults: () => {
      const result = document.createElement("li");
      result.setAttribute("class", "no_result");
      result.setAttribute("tabindex", "1");
      result.innerHTML = "No Results";
      document.querySelector("#autoComplete_list").appendChild(result);
    },
    // 結果が選択された場合
    onSelection: feedback => {
      const selection = feedback.selection.value.food;
      document.querySelector(".selection").innerHTML = selection;
      document.querySelector("#autoComplete").value = "";
      document
        .querySelector("#autoComplete")
        .setAttribute("placeholder", selection);
    }
  })
}

export default App;

```



### 4. 実行

```sh
$ yarn start
```

CSSを一切触っていないのでレイアウトはバラバラですが、ちゃんと動作しました！

![capture_autocomplete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/79c27a53-e829-8bd5-78e7-95c4fd66fe91.gif)





## オプション一覧

使用できるオプションの概要を書いておきます。
あまり多くないので、実際触ってみるのがわかりやすいと思います。

| キー           | 説明                                                         | 値                                                           | デフォルト                                                   |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| `data`         | データのsrc、データのkey、データのキャッシュ                 | **1- src**: 文字列の配列か、 文字列の配列を返す関数（**必須**）<br />**2- key**: 文字列の配列。srcがオブジェクトの場合、検索対象のkeyを指定するために必須。<br />**3- Cache**: srcが静的な場合はtrue、srcがクエリを使用した動的なものの場合はfalseを指定。 | Data `src`                                                   |
| `trigger`      | オートコンプリートする条件                                   | **1- event**: 文字列の配列<br />**2- condition**: - 関数 (query) => Condition `String` | 1- event: `["input"]`<br />2- condition: `(query.length > this.threshold && query !== " ")` |
| `query`        | 入力された文字列の加工                                       | オブジェクトを返す関数<br />1- manipulate: queryを引数に取る関数 | なし                                                         |
| `sort`         | Sort rendered results                                        | 関数<br />例 (a, b) => {<br />if (a.match < b.match) return -1;<br />if (a.match > b.match) return 1;<br />return 0;<br />} | ランダムな表示                                               |
| `placeHolder`  | フォームの初期値                                             | 文字列                                                       | Blank / Empty                                                |
| `selector`     | 対象とするinput要素のセレクタ                                | **-** idの文字列か、クラス **OR** **-** 関数 ( ) => `document.querySelector("")` | `"#autoComplete"`                                            |
| `threshold`    | Minimum characters length before engine starts rendering results | 数値                                                         | `0`                                                          |
| `debounce`     | 文字を入力してからオートコンプリートが開始されるまでの遅延時間 | ミリ秒の数値                                                 | `0`                                                          |
| `searchEngine` | Search Engine Type/Mode                                      | "strict"か"loose"、もしくは(query, record)を引数に取りそれぞれマッチしたものを返すカスタム関数 | `"strict"`                                                   |
| `resultsList`  | オートコンプリート結果のリストの表示先、位置、要素 position, element & navigation | ５つのメソッドからなるオブジェクト<br />1- render: 論理型<br />2- container: sourceを引数に取る関数<br />3- destination: `document.querySelector("#div")`<br />4- position: `"beforebegin"`, `"afterbegin"`, `"beforeend"`, `"afterend"`もしくは、 関数 ( ) => `{destination: "...", position: "..."}`<br />5- element: "ul", "span", "div",もしくはそれ以外<br /> 6- navigation:  `(event, input, resListElement, onSelection, resListData)` を引数に取る関数 | 1- render: `false` 2- container: `(source) => { ... }` 3- destination: `document.querySelector("#autoComplete")` 4- position: `"afterend"` 5- element: `"ul"` 6- navigation: `default` |
| `resultItem`   | 表示される結果の内容と要素                                   | **-** 2つのメソッドからなるオブジェクト<br />1- content:  (data, source) を引数に取る関数。`data.match`は、結果をハイライトするのに使用します。<br />2- element: "li", "span", "div"もしくはそれ以外 | 1- content: `(data, source) => { ... }`<br />2- element: `"li"` |
| `noResults`    | オートコンプリート結果が見つからなかった場合に実行される関数の定義 | 関数                                                         |                                                              |
| `highlight`    | 結果をハイライトするかどうか                                 | 論理型                                                       | `false`                                                      |
| `maxResults`   | 表示される結果の最大数                                       | 数値                                                         | `5`                                                          |
| `onSelection`  | 選択されたときに実行される関数の定義                         | 関数                                                         |                                                              |