5

I'm trying to filter multiple items at once. For example fruits and animals or even 3+. So far it only does it by selecting one item at a time.How can I select more than one? I have also tried https://wch.io/static/tagsort/demo-stacks/index.html but it was bugged keept showing the text but this how it should be but in javascript?

filterSelection("all")
function filterSelection(c) {
  var x, i;
  x = document.getElementsByClassName("filterDiv");
  if (c == "all") c = "";
  for (i = 0; i < x.length; i++) {
    w3RemoveClass(x[i], "show");
    if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
  }
}

function w3AddClass(element, name) {
  var i, arr1, arr2;
  arr1 = element.className.split(" ");
  arr2 = name.split(" ");
  for (i = 0; i < arr2.length; i++) {
    if (arr1.indexOf(arr2[i]) == -1) {element.className += " " + arr2[i];}
  }
}

function w3RemoveClass(element, name) {
  var i, arr1, arr2;
  arr1 = element.className.split(" ");
  arr2 = name.split(" ");
  for (i = 0; i < arr2.length; i++) {
    while (arr1.indexOf(arr2[i]) > -1) {
      arr1.splice(arr1.indexOf(arr2[i]), 1);     
    }
  }
  element.className = arr1.join(" ");
}
.filterDiv {
  float: left;
  background-color: #2196F3;
  color: #ffffff;
  width: 100px;
  line-height: 100px;
  text-align: center;
  margin: 2px;
  display: none;
}

.show {
  display: block;
}

.container {
  margin-top: 20px;
  overflow: hidden;
}
<h2>Filter DIV Elements</h2>

<input type="radio" onclick="filterSelection('all')" name="category" checked> Show all<br>
<input type="radio" onclick="filterSelection('cars')" name="category"> Cars<br>
<input type="radio" onclick="filterSelection('animals')" name="category"> Animals<br>
<input type="radio" onclick="filterSelection('fruits')" name="category"> Fruits<br>
<input type="radio" onclick="filterSelection('colors')" name="category"> Colors<br>

<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>
Aditi Parikh
  • 1,528
  • 3
  • 12
  • 33
Angel Ramirez
  • 77
  • 1
  • 6
  • You can use `querySelectorAll` instead and then pass array of possible classes and search as `.querySelectorAll(classes.join(', '))` and add classes in them – Rajesh Sep 20 '17 at 14:00

3 Answers3

1

// If we save our current state somewhere, we can easily filter the divs.
var checkedCategories = ["cars", "animals", "fruits", "colors"];
// We need a function that detects the click on a checkbox and adds/removes that category.
var changeCategory = function changeCategory(event) {
  // The event object will tell us exactly what was clicked.
  var checkbox = event.target;
  // The category we want toa dd or remove is the attribute
  var category = checkbox.getAttribute("data-category");
  // To check if we already have the category in the array, we just check the index.
  var savedCategoryIndex = checkedCategories.indexOf(category);
  // If the checkbox is checked, that category has to already exist in the array or get added.
  if (checkbox.checked && savedCategoryIndex === -1) {
    checkedCategories.push(category);
  }
  // if it is not checked and is present in the array, it needs to be removed.
  else if (!checkbox.checked && savedCategoryIndex !== -1) {
    checkedCategories.splice(savedCategoryIndex, 1);
  }
  renderCategories();
};
// We want a reusable function that will show/hide any categories we want to see.
var renderCategories = function renderCategories() {
  // We need a list of all the divs. So we just select all the divs that have the data-category attribute and slice them into an array.
  // Could be replaced by Array.from() if your browser supports it.
  var categoryDivs = Array.prototype.slice.call(document.querySelectorAll("div[data-category]"));
  // Now we need to loop over all the divs and check if they need to get hidden or not.
  categoryDivs.forEach(function(div) {
    // Get all the tags the div has
    var tags = div.getAttribute("data-category").split(" ");
    // If at least one tag of the div is inside our categories array, we know it'll need to get shown.
    var divShouldBeShown = tags.some(function(tag) {
      return checkedCategories.indexOf(tag) !== -1;
    });
    // The decide if we need to hide the div or not.
    // Can be replaced by a classList.toggle() if your browser supports it.
    if (divShouldBeShown && div.className.indexOf("hidden") !== -1) {
      div.className = div.className.replace("hidden", "");
    } else if (!divShouldBeShown && div.className.indexOf("hidden") === -1) {
      div.className = div.className + " hidden";
    }
  });
};
// Finally we have to add an event to the checkboxes.
document.querySelector("#categoryBoxes").addEventListener('click', changeCategory);
.hidden {
  display: none;
}
<!-- I've made some changed to the structure of your program to shorten the code alot -->
<h2>Filter DIV Elements</h2>
<!--
      We need checkboxes instead of radio buttons if we want to be able to select multiples.
      By wrapping them inside a div, we can use one event handler instead of one onclick event for each element.
      This makes adding more checkboxes later easier.
     -->
<div id="categoryBoxes">
  <input type="checkbox" data-category="cars" name="category" checked>Cars<br>
  <input type="checkbox" data-category="animals" name="category" checked>Animals<br>
  <input type="checkbox" data-category="fruits" name="category" checked>Fruits<br>
  <input type="checkbox" data-category="colors" name="category" checked>Colors<br>
</div>
<div class="container">
  <!--
       By using data-attributes instead of a classname, we make it easier to change the classname, no need to split/rejoin etc 
       This seperates the javascript from the css, so you can keep the css for styling only and the data-attribute for JS
      -->
  <div data-category="cars" class="filterDiv">BMW</div>
  <div data-category="colors fruits" class="filterDiv">Orange</div>
  <div data-category="cars" class="filterDiv">Volvo</div>
  <div data-category="colors" class="filterDiv">Red</div>
  <div data-category="cars animal" class="filterDiv">Mustang</div>
  <div data-category="colors" class="filterDiv">Blue</div>
  <div data-category="animals" class="filterDiv">Cat</div>
  <div data-category="animals" class="filterDiv">Dog</div>
  <div data-category="fruits" class="filterDiv">Melon</div>
  <div data-category="fruits animals" class="filterDiv">Kiwi</div>
  <div data-category="fruits" class="filterDiv">Banana</div>
  <div data-category="fruits" class="filterDiv">Lemon</div>
  <div data-category="animals" class="filterDiv">Cow</div>
</div>
G-Cyrillus
  • 85,910
  • 13
  • 85
  • 110
Shilly
  • 8,021
  • 1
  • 13
  • 22
  • hello, can you explain what you propose and modified ? (i would have used an id instead a data-attribute ;) ) – G-Cyrillus Sep 20 '17 at 14:42
  • Just trying to learn the OP to write easier code. No need to mess with splitting ids and classes when you can just use a data attribute to add any amount of tags to an element. You could use an id on the checkboxes, that's true. But ids won't work on the divs, since ids have to be unique. Imho, it's easier to think about what's happening if everythings nicely split into parts. The category array is like your data model, the render function a view and the event handler a controller. + it includes some array techniques that I was very happy reading about when I was messing with for loops. – Shilly Sep 20 '17 at 14:48
  • The entire explanation is in the code, but since you split it into a code snippet instead of just text the OP can copy, the explanation is out of order. :) – Shilly Sep 20 '17 at 14:52
  • You can reverse the edit if you wish , but you'll loose the demo ;) . id or data-attibute are attribute anyway. both works. i go for data-attribute when regular ones are used or not made for the purpose i wish. that was my personnal opinion. – G-Cyrillus Sep 20 '17 at 14:56
  • Here was the way i made it once via CSS only( https://codepen.io/gcyrillus/pen/JFdEk/ ). It doesn't hurt to build a proper form and you can submit your choice without javascript. I first learnt to write HTML/CSS via the standard and i'm not a develloper , this explains my point of view. – G-Cyrillus Sep 20 '17 at 15:04
  • To reverse edit, click on edite xx xx ago, and rollback to previous version – G-Cyrillus Sep 20 '17 at 15:10
0

var inputs = document.getElementsByTagName("input");
var cbs = []; //will contain all checkboxes

for (var i = 0; i < inputs.length; i++) {
  if (inputs[i].type == "checkbox") {
   cbs.push(inputs[i]);
  }
}

filterSelection();

function filterSelection() {
 var checkedVal = [],showAll = false;
 for (var i = 0; i < cbs.length; i++) {
  if (cbs[i].checked && cbs[i].value == "all") {
   var showAll = true;
   break;
  } else if (cbs[i].checked) {
   checkedVal.push(cbs[i].value);
  }
 }

 var x, i;
 x = document.getElementsByClassName("filterDiv");
 //if (c == "all") c = "";
 for (i = 0; i < x.length; i++) {
  w3RemoveClass(x[i], "show");
  if (!showAll) {
   for (var j = 0; j < checkedVal.length; j++) {
    if (x[i].className.indexOf(checkedVal[j]) > -1) {
     w3AddClass(x[i], "show");
    }
   }
  }
    else {
     w3AddClass(x[i], "show");
    }

 }
}



function w3AddClass(element, name) {
 var i, arr1, arr2;
 arr1 = element.className.split(" ");
 arr2 = name.split(" ");
 for (i = 0; i < arr2.length; i++) {
  if (arr1.indexOf(arr2[i]) == -1) {
   element.className += " " + arr2[i];
  }
 }
}

function w3RemoveClass(element, name) {
 var i, arr1, arr2;
 arr1 = element.className.split(" ");
 arr2 = name.split(" ");
 for (i = 0; i < arr2.length; i++) {
  while (arr1.indexOf(arr2[i]) > -1) {
   arr1.splice(arr1.indexOf(arr2[i]), 1);
  }
 }
 element.className = arr1.join(" ");
}
.filterDiv {
  float: left;
  background-color: #2196F3;
  color: #ffffff;
  width: 100px;
  line-height: 100px;
  text-align: center;
  margin: 2px;
  display: none;
}

.show {
  display: block;
}

.container {
  margin-top: 20px;
  overflow: hidden;
}
<h2>Filter DIV Elements</h2>

<div><input type="checkbox" onclick="filterSelection()" name="category" value="all" checked> Show all</div>
<input type="checkbox" onclick="filterSelection();" name="category" value="cars"> Cars<br>
<input type="checkbox" onchange="filterSelection();" name="category" value="animals"> Animals<br>
<input type="checkbox" onchange="filterSelection();" name="category" value="fruits"> Fruits<br>
<input type="checkbox" onchange="filterSelection()" name="category" value="colors"> Colors<br>

<div class="container">
  <div class="filterDiv cars show">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>

You can use check boxes instead of radio buttons to do so. Have one button called Apply which will trigger the filtering logic.

Else, if you don't want to use Apply button then you can use the technique used in https://wch.io/static/tagsort/demo-stacks/index.html Here, user is toggling classes based on the click on the span and depending on the clicked divs(having active class), you can build your filtering logic.

Hope this helps.

Piyush
  • 81
  • 5
  • the demo is not so great. it gives me extra butttons – Angel Ramirez Sep 21 '17 at 06:11
  • @AngelRamirez I have updated my answer with the working code. Have a look at it. We have used plain javascript to achieve the output. I would suggest you to use some library like JQuery. – Piyush Sep 22 '17 at 10:33
0

The attribute ID is missing on your inputs.

the attribute name groups the radio and allows only one to be checked at once but it will not help on its own to treat the inputs via a submit or javascript.

You can use CSS :checked to select siblings coming next if its only about visual(nothing submit or to treat via js) but it will still require an id in order to work as expected.

example with checkbox :

#all:checked  ~ .container .filterDiv{
  display: block;
}


.filterDiv, :not(#all):checked ~ .container  .filterDiv{
  float: left;
  background-color: #2196F3;
  color: #ffffff;
  width: 100px;
  line-height: 100px;
  text-align: center;
  margin: 2px;
  display: none;
}

#cars:checked ~ .container .cars,
#animals:checked ~ .container .animals,
#fruits:checked ~ .container .fruits,
#color:checked ~ .container .colors{
  display: block;
}

.container {
  margin-top: 20px;
  overflow: hidden;
}
<h2>Filter DIV Elements</h2>

<input type="checkbox"  id="all"  checked> Show all<br>
<input type="checkbox" id="cars"> Cars<br>
<input type="checkbox"  id="animals"> Animals<br>
<input type="checkbox"  id="fruits"  > Fruits<br>
<input type="checkbox" id="color" > Colors<br>

<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>

Here is an old pen of mine sorting gallerie via radio input and labels inside a form , so selection can be submited and treated on server side or via javascript. You can inspire yourself from the linked codepen where form and radio are used.

G-Cyrillus
  • 85,910
  • 13
  • 85
  • 110
  • yeah now the only problem im encountering is that when push down or active i want to change the color. So i made it a button the .WiFi:hover{background-color:black} works but the .WiFi:focus{background-color:black} does not. but yeah as a noob this checkbox i've havent never used. im mostly self tought by trial and error thanks again. if i cant do it in css im using jquery – Angel Ramirez Sep 22 '17 at 11:51
  • @AngelRamirez Do you want to make a new question and/or share the code so we see where it goes wrong :)? – G-Cyrillus Sep 22 '17 at 13:26