Search bar with working filter on items from json

Viewed 55

I'm trying to make a filter bar that will work with a search bar. My data is in from json format containing artist,title, src, etc. I can't really figure it out how to make it work properly. I got my results in console.log, but I'm stuck with making it visible in the search result box.

const results = document.querySelector(".search-results");
for (let i = 0; i < allMusic.length; i++) {
  let result = `<li li-index="${i + 1}" onclick="clicked(this)">
                <div class="result-box">
                  <div class="result-box-cover">
                  <h1 class="result-name">${allMusic[i].name}</h1>
                  <p class="result-artist">${allMusic[i].artist}</p>
                  </div>
                </div>
                <audio class="${allMusic[i].src}" src="songs/${allMusic[i].src}.mp3"></audio>
              </li>`;
  results.insertAdjacentHTML("beforeend", result);
}

function searchName() {
  input = document.getElementById('search-item');
  filter = input.value.toUpperCase();
  ul = document.getElementsByClassName("search-results");
  li = document.querySelectorAll('.search-results li');

  for (i = 0; i < li.length; i++) {
    nameResult = li[i].getElementsByClassName("result-name")[0];
    artistResult = li[i].getElementsByClassName("result-artist")[0];
    webkitresults = document.getElementsByClassName("search-results")[0];
    nameResult = nameResult.textContent || nameResult.innerText;
    artistResult = artistResult.textContent || artistResult.innerText;
    if (nameResult.toUpperCase().indexOf(filter) > -1) {
      li[i].style.display = "";
      li[i].style.visibility = "visible";
      webkitresults.classList.remove("webkit-hidden");
    }
    else if (artistResult.toUpperCase().indexOf(filter) > -1) {
      li[i].style.display = "";
      li[i].style.visibility = "visible";
      webkitresults.classList.remove("webkit-hidden");
    }
    else {
      li[i].style.display = "none";
      li[i].style.visibility = "hidden";
    }


    if (input.value.length == 0)
    {
      li[i].style.visibility = "hidden";
      webkitresults.classList.add("webkit-hidden");
    }
  }
}

function filterByName() {
  if (filterStatus.classList.contains("Hip-Hop")) {
    var result = allMusic.filter((x)=>x.style === "Hip-Hop");
    console.log(result);
  }
  if (filterStatus.classList.contains("Energic")) {
    var result = allMusic.filter((x)=>x.style === "Energic");
    console.log(result);
  }
}

function filterName(x) {
  filterStatus = document.querySelector(".search-filter");
  if (x==1)
  {
      filterStatus.classList.toggle("Hip-Hop");
      filterByName();
  }
  if (x==2)
  {
      filterStatus.classList.toggle("Energic");
      filterByName();
  }
  if (x==3)
  {
      filterStatus.classList.toggle("Workout");
      filterByName();
  }
  if (x==4)
  {
      filterStatus.classList.toggle("Chill");
      filterByName();
  }
  if (x==5)
  {
      filterStatus.classList.toggle("Sad-Lofi");
      filterByName();
  }
  if (x==6)
  {
      filterStatus.classList.toggle("Rock");
      filterByName();
  }
}

json file aka database for searchbar/filter


  {
      name: "Havana",
      artist: "Camila Cabello",
      src: "CamilaCabelloHavana",
      img: "CamilaCabelloHavana",
      style: "Energic",
      status: "",
  },
  {
      name: "Dj Is Your Second Name",
      artist: "C-Bool , Giang Pham",
      src: "C-BooLDJ",
      img: "C-BooLDJ",
      style: "Energic",
      status: "",
  },
  {
      name: "Never Go Away",
      artist: "C-Bool",
      src: "C-BooL-NeverGoAway",
      img: "C-BooL-NeverGoAway",
      style: "Energic",
      status: "",
  },
  {
      name: "Champions",
      artist: "Kevin Rudolf",
      src: "KevinRudolf-Champions",
      img: "KevinRudolf-Champions",
      style: "Energic",
      status: "",
  },
  {
      name: "Ride It",
      artist: "DJ Regard",
      src: "DJRegard-Rideit",
      img: "DJRegard-Rideit",
      style: "Energic",
      status: "",
  },

  <div class="search-content">

    <h1>Czego szukasz?</h1>
    <div class="search-bar">
      <input type="text" id="search-item" placeholder="Wykonawcy, utwory" onkeyup="searchName()">
        <div class="clear-btn">
        <button type="button" id="clear-input-btn" onclick="ClearFields();searchName();"><i class="fa-solid fa-xmark"></i></button>
        </div>
   </div>

  </div>

  <div class="search-filter" id="filterBox">

      <div class="search-item" onclick="filterName(1)">
        <p>Hip-Hop</p>
      </div>
      <div class="search-item" onclick="filterName(2)">
        <p>Energiczna</p>
      </div>
      <div class="search-item" onclick="filterName(3)">
        <p>Workout</p>
      </div>
      <div class="search-item" onclick="filterName(4)">
        <p>Chill</p>
      </div>
      <div class="search-item" onclick="filterName(5)">
        <p>Sad-Lofi</p>
      </div>
      <div class="search-item" onclick="filterName(6)">
        <p>Rock</p>
      </div>

  </div>

  <div class="search-results">



  </div>


</div>
1 Answers

Even without seeing your HTML it seems like there is scope for a lot of improvements. For example the filterName(x) function can be written as

function filterName(x) {
 if(x>0&&x<7){
  document.querySelector(".search-filter").classList.toggle(
   ["Hip-Hop","Energic","Workout","Chill","Sad-Lofi","Rock"][x-1]);
  filterByName();
 }
}

The rest can probably also be simplified, but knowing your HTML would be the key here.

The filterByName() function is also dubious in the way that it prints out two separate, unrelated results: firstly the search results for "Hip-Hop" (if that was selected) and secondly those for "Energic". Is your intention to filter according to one or all given criteria? Assuming that the match of one filter criterion is sufficient you could do:

function filterByName() {
  const classList=[...document.querySelector(".search-filter").classList];
  console.log(allMusic.filter(t=>classList.contains(t.style))
}

Let us piece your code together into a working snippet:

const allMusic = [
      {name: "Havana",artist: "Camila Cabello",src: "CamilaCabelloHavana",img: "CamilaCabelloHavana",style: "Energic",status: ""},
      {name: "Dj Is Your Second Name",artist: "C-Bool , Giang Pham",src: "C-BooLDJ",img: "C-BooLDJ",style: "Energic",status: ""},
      {name: "Never Go Away",artist: "C-Bool",src: "C-BooL-NeverGoAway",img: "C-BooL-NeverGoAway",style: "Energic",status: ""},
      {name: "Champions",artist: "Kevin Rudolf",src: "KevinRudolf-Champions",img: "KevinRudolf-Champions",style: "Energic",status: ""},
      {name: "Ride It",artist: "DJ Regard",src: "DJRegard-Rideit",img: "DJRegard-Rideit",style: "Hip-Hop",status: "" }];
const [search,filter,results] = ["#search-item",".search-filter",".search-results"].map(sel=>document.querySelector(sel));
results.innerHTML=allMusic.map((a,i)=>
`<li id="li${i}" onclick="clicked(this)">
  <div class="result-box">
   <div class="result-box-cover">
     <h1 class="result-name">${a.name}</h1>
     <p class="result-artist">${a.artist}</p>
   </div>
  </div>
  <a href="songs/${a.src}.mp3">${a.src}.mp3</a>
 </li>`).join("\n");
// show which item was clicked:
function clicked(o){console.log("Index "+o.id+" was clicked.");}
// main filter function
function filterList(){
  // get all styles by collecting the textContent of .active buttons:
  let styles=[...filter.querySelectorAll(".active")].map(b=>b.textContent),
  // get current search string from input field:
    srch=search.value.toLowerCase();
  allMusic.forEach((m,i)=>{ // both conditions must be met: style and search pattern
    results.children[i].style.display=(m.name+"|"+m.artist).toLowerCase().includes(srch)&&styles.includes(m.style)?"":"none";
  })
}
// attach filterList() to input event of text-input and click event of style buttons:
search.addEventListener("input",filterList);
filter.onclick = ev =>{
 if(ev.target.tagName == "BUTTON"){ 
   ev.target.classList.toggle("active");
   filterList()
}};
// do initial filtering:
filterList()
.active {
  background-color: #cec;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<div class="search-content">
  <h1>Czego szukasz?</h1>
  <div class="search-bar">
    <input type="text" id="search-item" placeholder="Wykonawcy, utwory">
    <button type="button" id="clear-input-btn" onclick="search.value='';filterList()"><i class="fa-solid fa-xmark"></i></button>
  </div>
</div>

<br>
<div class="search-filter" id="filterBox">
  <button>Hip-Hop</button>
  <button>Energic</button>
  <button>Workout</button>
  <button>Chill</button>
  <button>Sad-Lofi</button>
  <button>Rock</button>
</div>
<div class="search-results"></div>

I took the liberty of rewriting some of your script to avoid repetitions and generally to make the code a little shorter and easier to maintain. I also changed the <audio> tags to <a> as they would have been invisible otherwise.

Related