I have a product page that fetches the products from a database table, I am using foreach
to pull each product & each product displays as a card and using classes to filter them.
The problem is that the checkbox filter isnt working properly with multiple values (classes), if I have 2 classes the filter is only detecting the 1st class rather than all the classes(filters).
So for example, if I check the "Blue" checkbox I wont see any results since the JS is searching for a div that has only that one class ("filt-blue") but I need it to display all divs that contain "filt-blue" as well as any other filters that may be added.
See more on JSFIDDLE
function change() {
var checkboxes = document.getElementsByClassName('checkbox');
var chekboxInputs = Array.from(checkboxes).map(a => a.querySelector('input'));
var allAreUnselected = chekboxInputs.every(function(elem) {
return !elem.checked;
});
if (allAreUnselected) {
chekboxInputs.forEach(function(input) {
Array.from(document.querySelectorAll("." + input.getAttribute("rel"))).forEach(function(item) {
item.style.display = 'block';
});
});
} else {
chekboxInputs.forEach(function(input) {
Array.from(document.querySelectorAll("." + input.getAttribute("rel"))).forEach(function(item) {
item.style.display = input.checked ? 'block' : 'none';
});
});
}
}
change();
@media(max-width:768px){
.card{
width:100% !important;
margin:12px 6px !important;
}
}
.card {
text-align:left;
border-radius:4px;
margin:24px;
width:320px;
min-height:340px;
transition: all 0.2s;
border:var(--image-select-border);
}
.cards {
display: flex;
flex-wrap: wrap;
justify-content:center;
}
.card-body {
flex: 1 1 auto;
padding:12px;
}
.card-title {
margin-bottom:16px;
padding:12px;
}
@media(max-width:768px){
.card-img-top{
width:240px !important;
}
}
@media(max-width:768px){
.filtbtn{
width:90% !important;
margin-left:5% !important;
float:none !important;
}
}
.filterDiv {
display: none;
}
.show {
display: block;
}
@media(max-width:768px){
#opts{
margin-left:5% !important;
}
}
.optsel{
border-bottom:2px solid #0d6efd;
border-top: none;
border-left: none;
border-right: none;
}
.sidebar {
height:100%;
width:0;
position:fixed;
z-index:1;
top:76px;
right:0;
background-color:#111;
overflow-x:hidden;
transition: 0.5s;
padding-top: 60px;
}
.sidebar a {
padding:8px 12px;
text-decoration: none;
font-size:16px;
color: #818181;
display: block;
transition: 0.3s;
}
.sidebar .closebtn {
position: absolute;
top: 0;
margin-right:12px;
font-size:18px;
color:#222 !important;
background-color:#fff;
width:100%;
}
@media(max-width:768px){
.closebtn{
top:4px !important;
}
}
.openbtn {
font-size: 20px;
cursor: pointer;
background-color: #111;
color: white;
padding: 10px 15px;
border: none;
}
.openbtn:hover {
background-color: #444;
}
#main {
transition: margin-right .5s; /* If you want a transition effect */
padding: 20px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
@media screen and (max-height: 450px) {
.sidebar {padding-top: 15px;}
.sidebar a {font-size: 22px;}
}
.optbtn{
background-color:#fff;
color: #222;
cursor: pointer;
padding:14px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 16px;
transition: 0.4s;
}
.accordion {
background-color:#111;
color: #fff !important;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.4s;
}
.notaccordion {
background-color:#111;
color: #fff !important;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: center;
outline: none;
font-size:18px;
transition: 0.4s;
font-weight:bolder;
}
.active, .accordion:hover {
opacity:0.9;
}
.accordion:after {
content: '02B';
color: #777;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "2212";
}
.filterpanel {
padding:0 18px;
background-color:#fff;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}
.checkbox{
color:#222 !important;
padding:12px 12px;
}
.checkbox-button {
cursor: pointer;
}
.checkbox span{
margin-left:12px;
}
.checkbox input[type=checkbox] {
box-sizing: border-box;
padding: 0;
}
.checkbox input {
font-size: 1rem;
line-height: 1.5;
padding: 11px 23px;
border: 1px solid black;
border-radius: 0;
outline: 0;
background-color: transparent;
}
.checkbox-button__input {
opacity: 0;
position: absolute;
}
.checkbox-button__control {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
vertical-align: middle;
background-color: inherit;
color: #017b5f;
border: 2px solid #666;
}
.checkbox-button__input:checked+.checkbox-button__control:after {
content: "";
display: block;
position: absolute;
top: 2px;
left: 2px;
width: 12px;
height: 12px;
background-color:#0d6efd;
}
.checkbox-button__input:checked+.checkbox-button__control {
border-color:black;
border:2px solid black;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous" id="bootstrap-css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-1" name="choice1" onchange="change()" rel="filt-blue">
<span class="checkbox-button__control"></span>
</label>
<span><b>blue</b></span>
</div>
</div>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-2" name="choice1" onchange="change()" rel="filt-red">
<span class="checkbox-button__control"></span>
</label>
<span><b>red</b></span>
</div>
</div>
<hr>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-1a" name="choice2" onchange="change()" rel="filt-long">
<span class="checkbox-button__control"></span>
</label>
<span><b>long</b></span>
</div>
</div>
<div class="filter">
<div class="checkbox">
<label class="checkbox-button">
<input type="checkbox" class="checkbox-button__input" id="choice1-2a" name="choice2" onchange="change()" rel="filt-short">
<span class="checkbox-button__control"></span>
</label>
<span><b>short</b></span>
</div>
</div>
<div class="cards">
<div class="card filt-blue filt-long">
<h6 class="card-title"><b><?php echo $fetch['title'];?></b></h6>
<center><img src="/images/products/<?php echo $fetch['Img'];?>" class="card-img-top" style="width:160px;margin:0 auto;"></center>
<div class="card-body">
<small><?php echo $fetch['detail'];?></small>
<h6><b>Starting from - £<?php echo $fetch['SFprice'];?></b></h6>
</div>
</div>
<div class="card filt-blue filt-short">
<h6 class="card-title"><b><?php echo $fetch['title'];?></b></h6>
<center><img src="/images/products/<?php echo $fetch['Img'];?>" class="card-img-top" style="width:160px;margin:0 auto;"></center>
<div class="card-body">
<small><?php echo $fetch['detail'];?></small>
<h6><b>Starting from - £<?php echo $fetch['SFprice'];?></b></h6>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
2
Answers
This code should help your filter take into account multiple classes. There’s a couple of ways you can filter. For example, if you have "long and blue" do you want to show ONLY long and blue? Or would you want to show a "Red Long" as well. This particular code takes into account multiple filter classes but in a logical OR fashion instead of an AND logic.
Why doesn’t the current code work?
Your code iterates over every
input
, finding all cards matching thatinput
. The firstinput
isblue
, and the code iterates over all cards and correctly displays those matchingblue
. But it then continues to the next iteration, thered
checkbox. No cards match and so they are both immediately hidden. So any matching card(s) are actually shown and then immediately hidden.OK, how do we fix it?
Here’s my approach:
Notes
It is generally considered good practice to separate your HTML and JS. Here that would mean removing the inline
onchange="change()"
in the HTML, and replacing them with an event handler in the JS:You will make it so much easier for others to help if you can create a minimal, complete, and verifiable example of the problem. In this case a good chunk of the time I spent on this problem was evaulating what I could strip out – a lot of the HTML and most of the CSS classes are irrlevant. The PHP obviously does not work, and none of the CSS matters. Neither Bootstrap nor jQuery CSS or JS refs are necessary.
When you strip out everything not related to the problem it is that much easier to understand, debug, and work with.