Search code examples
javascriptfiltercheckbox

Multiple choice checkboxes as filters in JS but filters don't apply


I'm building an overview of plants with HTML, SCSS and JS and the user should have the option to filter plants by their need of light. It works fine, that the plants are displayed as cards but checking the checkbox does absolutely nothing.

This is what the checkboxes in HTML look like:

<div class="multiple-choice">
        <span><b>Light:</b><br/></span>
        <label for="halbschattig">
          <input type="checkbox" name="checklight" value="0" id="halbschattig">
          halbschattig
        </label><br/>
        <label for="hell">
          <input type="checkbox" name="checklight" value="1" id="hell">
          hell
        </label><br />
        <label for="sonnig">
          <input type="checkbox" 
          name="checklight" value="2" id="sonnig">
          sonnig
        </label>
      </div>

I stored all information about the plants in a JSON file, fetch it and then they get displayed. This is my JS code to fetch and display the data:

let plantsArr = [];
const plantcards = document.getElementById("plantcards");

let allCheckboxes = document.querySelectorAll("input[type=checkbox]");
let plantcard = Array.from(document.querySelectorAll(".plantcard"));
let checked = {};

async function getPlants() {
  try {
    const response = await fetch("../files/pflanzen.json", {
      method: "GET",
    });

    if (!response.ok) {
      throw new Error(`Error! status: ${response.status}`);
    }

    let data = await response.json();

    for (const key in data) {
      {
        plantsArr.push(data[key]);
      }
    }

    return data;
  } catch (error) {
    console.log(error);
  }
}

/* Load plants */

getPlants().then((data) => {
  data.forEach((data) => {
    plantcards.insertAdjacentHTML(
      "afterbegin",
      `<article class="plantcard">
        <div class="article-wrapper">
          <figure>
            <img src="../images/plant_types/${data.Image}" alt="${data.Name}" />
          </figure>
          <div class="article-body">
            <h2>${data.Name}</h2>
            <h3>${data.NameLatin}</h3>
            <p>
               Standort: ${getPlantLight(data.Light)}<br/>
            </p>
            <a href="#" class="read-more">
              Read more <span class="sr-only">about this is some title</span>
              <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 20 20" fill="currentColor">
                <path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd" />
              </svg>
            </a>
          </div>
        </div>
      </article>`
    );
  });
});

I tried to apply the filter (at the moment only for the light demand until this works) like the following:

getChecked("checklight");

Array.prototype.forEach.call(allCheckboxes, function (el) {
  el.addEventListener("change", toggleCheckbox);
});

function toggleCheckbox(e) {
  getChecked(e.target.name);
  setVisibility();
}

function getChecked(name) {
  checked[name] = Array.from(
    document.querySelectorAll("input[name=" + name + "]:checked")
  ).map(function (el) {
    return el.value;
  });
}

function setVisibility() {
  plantcard.map(function (el) {
    let checklight = checked.checklight.length
      ? _.intersection(Array.from(el.classList), checked.checklight).length
      : true;

    if (checklight) {
      el.style.display = "block";
      console.log("block");
    } else {
      el.style.display = "none";
      console.log("none");
    }
  });
}

Nothing happens in the console. If anyone might have a look at this and could give me a hint what I'm missing I'd be very grateful! :)


Solution

  • You can show/hide plant cards using CSS selectors. When one of the check boxes change value, you can update className of the #plantcards element. Based on that the cards will display or not.

    const plantcards = document.getElementById("plantcards");
    let plantsArr = [];
    
    document.forms.light.addEventListener('change', e => {
      let form = e.target.form;
      plantcards.className = '';
      [...form.elements]
        .filter(input => input.checked)
        .forEach(input => plantcards.classList.add(input.name));
    });
    
    async function getPlants() {
      try {
        const response = await fetch("data:application/json;base64,eyJQbGFudDEiOnsiSW1hZ2UiOiJpbWcwMS5wbmciLCJOYW1lIjoiTmFtZTAxIiwiTmFtZUxhdGluIjoiTmFtd2VMYXRpbjAxIiwiTGlnaHQiOiJsaWdodCJ9LAoiUGxhbnQyIjoKeyJJbWFnZSI6ImltZzAyLnBuZyIsIk5hbWUiOiJOYW1lMDIiLCJOYW1lTGF0aW4iOiJOYW13ZUxhdGluMDIiLCJMaWdodCI6ImxpZ2h0In0sCiJQbGFudDMiOgp7IkltYWdlIjoiaW1nMDMucG5nIiwiTmFtZSI6Ik5hbWUwMyIsIk5hbWVMYXRpbiI6Ik5hbXdlTGF0aW4wMyIsIkxpZ2h0IjoiZGFyayJ9LAoiUGxhbnQ0IjoKeyJJbWFnZSI6ImltZzA0LnBuZyIsIk5hbWUiOiJOYW1lMDQiLCJOYW1lTGF0aW4iOiJOYW13ZUxhdGluMDQiLCJMaWdodCI6InNoYWRlIn0sCiJQbGFudDUiOgp7IkltYWdlIjoiaW1nMDUucG5nIiwiTmFtZSI6Ik5hbWUwNSIsIk5hbWVMYXRpbiI6Ik5hbXdlTGF0aW4wNSIsIkxpZ2h0IjoibGlnaHQifSwKIlBsYW50NiI6CnsiSW1hZ2UiOiJpbWcwNi5wbmciLCJOYW1lIjoiTmFtZTA2IiwiTmFtZUxhdGluIjoiTmFtd2VMYXRpbjA2IiwiTGlnaHQiOiJkYXJrIn0sCiJQbGFudDciOgp7IkltYWdlIjoiaW1nMDcucG5nIiwiTmFtZSI6Ik5hbWUwNyIsIk5hbWVMYXRpbiI6Ik5hbXdlTGF0aW4wNyIsIkxpZ2h0Ijoic2hhZGUifSwKIlBsYW50OCI6CnsiSW1hZ2UiOiJpbWcwOC5wbmciLCJOYW1lIjoiTmFtZTA4IiwiTmFtZUxhdGluIjoiTmFtd2VMYXRpbjA4IiwiTGlnaHQiOiJsaWdodCJ9Cn0=", {
          method: "GET",
        });
    
        if (!response.ok) {
          throw new Error(`Error! status: ${response.status}`);
        }
    
        let data = await response.json();
        for (const key in data) {
          {
            plantsArr.push(data[key]);
          }
        }
    
        return plantsArr;
      } catch (error) {
        console.log(error);
      }
    }
    
    getPlants().then((data) => {
      data.forEach((data) => {
        plantcards.insertAdjacentHTML(
          "afterbegin",
          `<article class="plantcard ${data.Light}">
            <div class="article-wrapper">
              <figure>
                <img src="../images/plant_types/${data.Image}" alt="${data.Name}" />
              </figure>
              <div class="article-body">
                <h2>${data.Name}</h2>
                <h3>${data.NameLatin}</h3>
                <p>
                   Standort: ${getPlantLight(data.Light)}<br/>
                </p>
                <a href="#" class="read-more">
                  Read more <span class="sr-only">about this is some title</span>
                  <svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 20 20" fill="currentColor">
                    <path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd" />
                  </svg>
                </a>
              </div>
            </div>
          </article>`
        );
      });
    });
    
    function getPlantLight(str) {
      return str;
    }
    .multiple-choice {
      display: flex;
      flex-direction: column;
    }
    
    .bold {
      font-weight: bold;
    }
    
    .icon {
      width: 1em;
    }
    
    #plantcards article {
      display: none;
    }
    
    #plantcards.checklight article.light,
    #plantcards.checkshade article.shade,
    #plantcards.checkdark article.dark {
      display: block;
    }
    <form name="light">
      <div class="multiple-choice">
        <div class="bold">Light:</div>
        <label>
        <input type="checkbox" name="checkshade">
        halbschattig
      </label>
        <label>
        <input type="checkbox" name="checkdark">
        hell
      </label>
        <label>
        <input type="checkbox" name="checklight">
        sonnig
      </label>
      </div>
    </form>
    <section id="plantcards"></section>