javascripthtmlsearchsplithtml-lists

Multiple word search feature HTML list filter


I have a search feature which uses JavaScript to filter a html list.

All list items are hidden by default but show if the user inputs the right keywords.

At the moment the search only returns items which match the input exactly.

If a word is missed, additional word added or order of words differ, the result isn’t shown.

I’d like the user to be able to search multiple words, and if one or more of the words match, the result is shown.

It should not matter which order the user inputs the words.

For example, searching ‘VAUXHALL CORSA DIESEL’ would return the list item, ‘BLUE VAUXHALL CORSA 1.6L DIESEL.’

Searching ‘DIESEL VAUXHALL’ would also return the same list item.

I figured this could be done by incorporating .split(“ ”), however I am unsure at what point in the code it is needed.

I’ve tried a few configurations to no avail.

I’m very new to coding!

I’ve added the current script.

window.addEventListener("load", () => {
  var filter = document.getElementById("filter"),
    list = document.querySelectorAll(".list li");
  filter.onkeyup = () => {
    let search = filter.value.toLowerCase();
    for (let i of list) {
      let item = i.innerHTML.toLowerCase();
      if (item.indexOf(search) == -1) {
        i.classList.add("hide");
      } else {
        i.classList.remove("hide");
        if (filter.value.length == 0) {
          i.classList.add("hide");
        }
      }
    }
  };
});


Solution

  • I guessed some HTML

    This does what you ask using .every and .includes

    I also use on input instead since the user can paste into the field Lastly I simplified the hiding

    window.addEventListener("load", () => {
      const list = document.querySelector(".list")
      filter = document.getElementById("filter"),
        lis = [...list.querySelectorAll("li")]; // spread to use map and sort
      filter.addEventListener("input", () => {
        let search = filter.value.toLowerCase().split(/\W+/);
        lis.forEach(li => {
          if (search.length === 0) {
            li.hidden = true;
            li.dataset.rank = 0;
            return
          }
          let items = li.textContent.toLowerCase().split(/\W+/)
          li.hidden = !search.every(flt => items.includes(flt))
          li.dataset.rank = search.filter(flt => items.includes(flt)); // how many
        });
        lis.sort((a, b) => a.dataset.rank - b.dataset.rank).forEach(li => list.append(li))
      });
    });
    For example, searching ‘VAUXHALL CORSA DIESEL’ would return the list item, Searching ‘DIESEL VAUXHALL’ would also return the same list item.
    <hr/>
    <input type="text" id="filter" />
    <ul class="list">
      <li>BLUE VAUXHALL CORSA 1.6L DIESEL</li>
      <li>RED DIESEL</li>
      <li>GREEN VAUXHALL ASTRA 1.6L PETROL</li>
      <li>RED VAUXHALL CORSA 1.6L DIESEL</li>
      <li>RED VAUXHALL CORSA 1.6L PETROL</li>
      <li>RED VAUXHALL ASTRA 1.6L PETROL</li>
      <li>RED VAUXHALL ASTRA 1.6L DIESEL</li>
    
    </ul>