Datalistの挙動が気に入らなかったのでそれっぽいの作ってみた

はじめに

本記事はProgaku Advent Calendar 2022の12日目の記事です。

作ったきっかけ

最初はselect使ってセレクトボックスだったんだけれど、雇い主が「テキストボックスに入力できるようにして、あいまい検索にしてほしい。けどライブラリは管理面倒だから使わないでくれ。」って言ってて、でもdatalistは挙動いまいちだしなぁ。って思って、それっぽいの作ってみた。

ここにのせるのは実際に作ったやつの機能縮小版・簡易版という感じ。

いろいろな動作の確認とかはしてないので、そのままコピペとかは多分無理。

サンプル

codepen

コード

<div class="input-box">
  <input type="text" class="input" autocomplete="off">
  <div class="list">
    <ul>
      <li class="li">りんご</li>
      <li class="li">ぶどう</li>
      <li class="li">みかん</li>
    </ul>
  </div>
</div>
body {
  height: 400px;
}

li {
  list-style: none;
}

ul {
  padding: 0;
  margin: 0;
}

.input {
  border: 1px solid black;
  border-radius: 4px;
  padding: 5px 5px;
  width: 182px;
}

.input-box {
  position: relative;
}

.list {
  position: absolute;
  width: 192px;
  top: 32px;
  border: 1px solid black;
  border-radius: 8px;
  display: none;
}

.list-show {
  display: block;
}

.li {
  border-radius: 8px;
}

.li:hover {
  background-color: rgba(167, 167, 167, 0.8);
}

.li-hide {
  display: none;
}

const list = document.querySelector('.list');
const li = document.querySelectorAll('.li');
const input = document.querySelector('.input');
let inputIndexOf;

input.addEventListener('focus', () => {
  inputListAndItemShowToggle();

  document.body.addEventListener('click', (e) => {
    if (list.classList.contains('list-show')) {
      if (!e.target.closest('.list') && !e.target.closest('.input')) {
        list.classList.remove('list-show');
      }
    }
  });
});

li.forEach(function(element){
  element.addEventListener('click', () => {
    list.classList.remove('list-show');
    input.value = element.innerText;
  });
});

input.addEventListener('input', () => {
  inputListAndItemShowToggle();
});

function inputListAndItemShowToggle() {
  inputIndexOf = 0;
  li.forEach(function(element){
    if (element.innerText.indexOf(input.value) !== -1) {
      if (element.classList.contains('li-hide')) {
          element.classList.remove('li-hide');
      }
      inputIndexOf += 1;
    } else {
      if (!element.classList.contains('li-hide')) {
          element.classList.add('li-hide');
      }
    }
  });

  if (inputIndexOf === 0) {
    if (list.classList.contains('list-show')) {
      list.classList.remove('list-show');
    }
  } else if (inputIndexOf > 0) {
    if (!list.classList.contains('list-show')) {
      list.classList.add('list-show');
    }
  }
}

説明

テキストボックスで文字を入力するたびにリストのアイテムと文字列が一致するか調べて、一致しないときは非表示にしてる。

リストのアイテムを選択するとそれがテキストボックスに入る。