Jump to content

Appling multiple filters to div elements


Marjo

Recommended Posts

Hi all!

I've been looking at this code snippet that lets you filter div elements based on their class name: https://www.w3schools.com/howto/howto_js_filter_elements.asp 
I'm wondering how you would go about applying multiple filters to some elements, similar to turning filters on and off, or using boolean operators like AND, OR or NOT. 

For example (using the dataset used in the link above): assume you want to filter out all the elements that are animals AND whose name is 3 letters long ("cat", "dog" and "cow"). I know it's a silly example, it's just to get my point across. 😅
If you'd make a separate class name for the length of the name, as I understand it you'd still only be able to filter for elements that are animals ("mustang", "cat", "dog", "kiwi" and "cow") OR that have 3 letters ("red", "blue", "cat", "dog" and "cow") - you can't combine both filters.

Is there an elegant way to accomplish this that's similar to the code used in the site's example? Or would you have to change your entire approach?
(Or is this not possible at all and would you be stuck having to make a separate class name for each and every combination? 😨 That... doesn't sound very practical.)

Thanks in advance!

Link to comment
Share on other sites

I'd probably use a different approach.

You'll need different code depending on whether you want to choose the intersection of the groups or the union of the groups, or you could have some radio options to choose either an intersection or a union.

The general idea is that, when you change any of the options, you will run a function. In this function, you will loop through each of the elements, hide it if it does not meet the criteria, show it otherwise. Instead of using buttons, you will need checkboxes so that you can select more than one of them at a time without having to write too much Javascript. CSS can make the actual checkbox invisible while still styling the clickable label.

The result would be something like this:

<div class="options">
  <label><input type="radio" name="operation" value="union" id="op-union" checked> Union</label>
  <label><input type="radio" name="operation" value="intersection" id="op-inter"> Intersection</label>
</div>

<div class="categories">
  <span class="category">
    <input type="checkbox" value="cars" id="cat-cars">
    <label for="cat-cars">Cars</label>
  </span>
  <span class="category">
    <input type="checkbox" value="animals" id="cat-animals">
    <label for="cat-animals">Animals</label>
  </span>
  <span class="category">
    <input type="checkbox" value="fruits" id="cat-fruits">
    <label for="cat-fruits">Fruits</label>
  </span>
  <span class="category">
    <input type="checkbox" value="colors" id="cat-colors">
    <label for="cat-colors">Colors</label>
  </span>
</div>

<div class="container">
  <div class="filterDiv cars">BMW</div>
  <div class="filterDiv colors fruits">Orange</div>
  <div class="filterDiv cars">Volvo</div>
  <div class="filterDiv colors">Red</div>
  <div class="filterDiv cars animals">Mustang</div>
  <div class="filterDiv colors">Blue</div>
  <div class="filterDiv animals">Cat</div>
  <div class="filterDiv animals">Dog</div>
  <div class="filterDiv fruits">Melon</div>
  <div class="filterDiv fruits animals">Kiwi</div>
  <div class="filterDiv fruits">Banana</div>
  <div class="filterDiv fruits">Lemon</div>
  <div class="filterDiv animals">Cow</div>
</div>


<script>
var checkboxes = document.querySelectorAll(".categories input");
for(var i = 0; i < checkboxes.length; i++) {
  checkboxes[i].addEventListener("change", filter);
}
var radios = document.getElementsByName("operation");
for(var i = 0; i < radios.length; i++) {
  radios[i].addEventListener("change", filter);
}
filter();
function filter() {
  var i, j;

  // Choose an operation
  var operation = document.getElementById("op-union").checked ? "union" : "intersection";

  // Get the selected categories
  var checkboxes = document.querySelectorAll(".categories input");
  var categories = [];
  var c;
  for(i = 0; i < checkboxes.length; i++) {
    if(checkboxes[i].checked) {
      c = checkboxes[i].value;
      categories.push(c);
    }
  }

  // Apply the filter
  var items = document.querySelectorAll(".filterDiv");
  var item, show;
  for(i = 0; i < items.length; i++) {
    item = items[i];
    if(categories.length == 0) {
      show = true;
    } else if(operation == "union") {
      // Union: Only one of the categories needs to exist
      show = false;
      for(j = 0; j < categories.length; j++) {
        if(item.classList.contains(categories[j])) {
          show = true;
          break;
        }
      }
    } else {
      // Intersection: All of the categories must apply
      show = true;
      for(j = 0; j < categories.length; j++) {
        if(!item.classList.contains(categories[j])) {
          show = false;
          break;
        }
      }
    }

    if(show) {
      item.classList.add("show");
    } else {
      item.classList.remove("show");
    }
  }
}
</script>

 

Link to comment
Share on other sites

You can create a data- attribute to hold 1 or more details related to each item, then its just a matter of filtering the values in data attribute. For instance for your example you could loop through all divfilter classes get textContent length and add to data-details attribute, as data-details="3" which is number of characters, add additional like colours data-details='["3","black"]'; and so on.

Link to comment
Share on other sites

Thank you both for your input! 

@Ingolme I tried out your code, but I think I'm overlooking something because it doesn't seem to actually filter anything. It might be something obvious, as I'm very new to JS. 

Is there still something I'm supposed to add for this code to work (apart from CSS for styling)? 

Link to comment
Share on other sites

1 hour ago, Ingolme said:

My code should work as long as you keep the same CSS as before. Without that CSS, it won't hide the elements that should be filtered.

*facepalm*

Yep, no, you're absolutely right, I accidentally messed up part of the CSS when I tested this earlier 🥴  Works like a charm now, thanks a ton!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...