I have a table that i need to filter based on multiple columns, the example i have uses checkboxes to filter on 5 columns, some of the columns are hidden and are not displayed in the table but can still be filtered on.
Example of how I need it to work:
-
all rows will show as a default if no filters are selected, currently the table is blank by default, and you need to check the boxes to show rows.
-
if MONTH filter ‘MAR’ is checked, the 4 rows that meet that criteria will show, I then add the LANGUAGE filter ‘French’ and ‘Spanish’ and the only the 3 rows that meet both filter criteria will show. I then uncheck the LANGUAGE filters and check the HOUSE filter ‘None’ only the 1 row that meets the new criteria is shown.
There are a few problems in my current attempt like the filter categories are not correctly ANDed together, so when ‘Ron’ and ‘Mar’ are selected the results include other Names that do not match ‘Ron’. Another issue it that when the filters are cleared, many rows that were hidden due to the previous filter do not become visible.
Usable example is here:
https://codepen.io/ashleigh-leader/pen/BaXvOqO
function filter(event, filterCol) {
let element = event.target;
let condt1 = document.getElementsByClassName(filterCol);
for (let i = 0; i < condt1.length; i++) {
if (condt1[i].innerHTML.toLowerCase() == element.value.toLowerCase()) {
if (element.checked == true) {
condt1[i].parentElement.style = ""
} else {
condt1[i].parentElement.style = "display:none"
}
}
}
}
document.querySelectorAll('.option1')
.forEach(input => input.addEventListener('input', ()=>filter(event,"check1")));
document.querySelectorAll('.option2')
.forEach(input => input.addEventListener('input', ()=>filter(event,"check2")));
document.querySelectorAll('.option3')
.forEach(input => input.addEventListener('input', ()=>filter(event,"check3")));
document.querySelectorAll('.option4')
.forEach(input => input.addEventListener('input', ()=>filter(event,"check4")));
document.querySelectorAll('.option5')
.forEach(input => input.addEventListener('input', ()=>filter(event,"check5")));
#myTable {
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 14px;
margin: 5px;
}
#myTable th, #myTable td {
text-align: left;
padding: 12px;
}
#myTable tr {
border-bottom: 1px solid #ddd;
color: #58585B;
font-family: "museo-sans-rounded";
font-size: 14px;
font-weight: 300px;
}
#myTable tr.header {
background-color: #862B90;
background-size: contain;
color: white;
font-family: "museo-sans-rounded";
font-size: 16px;
font-weight: 300px;
}
#myTable tr:hover {
font-family: "museo-sans-rounded";
font-size: 16px;
font-weight: 300px;
}
#myTable td.month-pill {
align-self: center;
background-color: #FFC52E;
border: none;
color: #FFC52E;
padding: 10px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px;
border-radius: 16px;
}
#myTable td.month {
color: #FFC52E;
align-items: center;
}
.filter-container {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 20%;
}
.filter-title {
font-family: "museo-sans-rounded";
color: #58585B !important;
font-size: 14px;
font-weight: 500px;
line-height: 1.25;
text-align: left;
}
.filter-option {
font-family: "museo-sans-rounded";
color: #58585B !important;
font-size: 13px;
font-weight: 300px;
line-height: 1.5;
text-align: left;
}
.checkbox.style-b {
display: inline-block;
position: relative;
padding-left: 30px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
padding-top: 4px;
}
.checkbox.style-b input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkbox.style-b input:checked ~ .checkbox__checkmark {
background-color: #fff;
}
.checkbox.style-b input:checked ~ .checkbox__checkmark:after {
opacity: 1;
}
.checkbox.style-b:hover input ~ .checkbox__checkmark {
background-color: #eee;
}
.checkbox.style-b:hover input:checked ~ .checkbox__checkmark {
background-color: #fff;
}
.checkbox.style-b .checkbox__checkmark {
position: absolute;
top: 3px;
left: 0;
height: 20px;
width: 20px;
background-color: #fff;
border: 1px solid #8F8F8F;
transition: background-color 0.25s ease;
border-radius: 4px;
}
.checkbox.style-b .checkbox__checkmark:after {
content: "";
position: absolute;
left: 8px;
top: 3px;
width: 7px;
height: 12px;
border: solid #862B90;
border-width: 0 3px 3px 0;
transform: rotate(45deg);
opacity: 0;
transition: opacity 0.25s ease;
}
.checkbox.style-b .checkbox__body {
color: #212529;
font-feature-settings: 'clig' off, 'liga' off;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
}
<div id="input">
<div class="filter-container" style="display: flex; flex-direction: column; align-items: flex-start; width: 20%;">
<p>CHECKBOX FILTERS</p>
<p class="filter-title">ID</p>
<lable><input class="option1" type="checkbox" value="01">01</label>
<lable><input class="option1" type="checkbox" value="02">02</label>
<lable><input class="option1" type="checkbox" value="03">03</label>
<lable><input class="option1" type="checkbox" value="04">04</label>
<p class="filter-title">Name</p>
<lable><input class="option2" type="checkbox" value="Harry">Harry</label>
<lable><input class="option2" type="checkbox" value="Ron">Ron</label>
<lable><input class="option2" type="checkbox" value="Malfoy">Malfoy</label>
<lable><input class="option2" type="checkbox" value="Hagrid">Hagrid</label>
<p class="filter-title">Month</p>
<lable><input class="option3" type="checkbox" value="JAN">JAN</label>
<lable><input class="option3" type="checkbox" value="FEB">FEB</label>
<lable><input class="option3" type="checkbox" value="MAR">MAR</label>
<lable><input class="option3" type="checkbox" value="APR">APR</label>
<lable><input class="option3" type="checkbox" value="MAY">MAY</label>
<lable><input class="option3" type="checkbox" value="JUN">JUN</label>
<p class="filter-title">House</p>
<lable><input class="option4" type="checkbox" value="Gryffindor">Gryffindor</label>
<lable><input class="option4" type="checkbox" value="Slytherin">Slytherin</label>
<lable><input class="option4" type="checkbox" value="None">None</label>
<p class="filter-title">Language</p>
<lable><input class="option4" type="checkbox" value="English">English</label>
<lable><input class="option4" type="checkbox" value="Spanish">Spanish</label>
<lable><input class="option4" type="checkbox" value="French">French</label>
</div>
<p>FILTER TABLE</p>
<table id="myTable" class="myTable">
<thead>
<tr class="header">
<th style="width: 3%; text-align: left; padding: 12px;">ID</th>
<th style="width: 37%; text-align: left; padding: 12px;">Name</th>
<th style="width: 5%; text-align: center; padding: 12px;">JAN</th>
<th style="width: 5%; text-align: center; padding: 12px;">FEB</th>
<th style="width: 5%; text-align: center; padding: 12px;">MAR</th>
<th style="width: 5%; text-align: center; padding: 12px;">APR</th>
<th style="width: 5%; text-align: center; padding: 12px;">MAY</th>
<th style="width: 5%; text-align: center; padding: 12px;">JUN</th>
</thead>
<tbody>
<tr>
<td class="check1">01</td>
<td class="check2">Harry</td>
<td class="check3"></td>
<td class="check3">FEB</td>
<td class="check3">MAR</td>
<td class="check3">APR</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check4" style="display: none;">Griffindor</td>
<td class="check5" style="display: none;">Spanish</td>
<td class="check5" style="display: none;">French</td>
</tr>
<tr>
<td class="check1">01</td>
<td class="check2">Harry</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3">APR</td>
<td class="check3">MAY</td>
<td class="check3">JUN</td>
<td class="check4" style="display: none;">Griffindor</td>
<td class="check5" style="display: none;">Engligh</td>
<td class="check5" style="display: none;">Spanish</td>
</tr>
<tr>
<td class="check1">02</td>
<td class="check2">Ron</td>
<td class="check3">JAN</td>
<td class="check3"></td>
<td class="check3">MAR</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check4" style="display: none;">Spanish</td>
</tr>
<tr>
<td class="check1">03</td>
<td class="check2">Hagrid</td>
<td class="check3">JAN</td>
<td class="check3">FEB</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3">JUN</td>
<td class="check4" style="display: none;">None</td>
<td class="check5" style="display: none;">Engligh</td>
</tr>
<tr>
<td class="check1">03</td>
<td class="check2">Hagrid</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3">MAR</td>
<td class="check3">APR</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check4" style="display: none;">None</td>
<td class="check5" style="display: none;">Engligh</td>
</tr>
<tr>
<td class="check1">04</td>
<td class="check2">Malfoy</td>
<td class="check3">JAN</td>
<td class="check3">FEB</td>
<td class="check3">MAR</td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check3"></td>
<td class="check4" style="display: none;">Slytherin</td>
<td class="check5" style="display: none;">Engligh</td>
<td class="check5" style="display: none;">Spanish</td>
<td class="check5" style="display: none;">French</td>
</tr>
</tbody>
</table>
</div>
2
Answers
The problem with your current logic is that you only evaluate the columns against each input explicitly for the filter input that was just changed.
Your rules only need to match one of the selected options, your code hides rows if they contain a cell that doesn’t match, so if
JAN
is selected, rows that includeFEB
andMAR
as well asJAN
would be filtered out.Current code for un-selecting filters doesn’t validate the other column conditions, so un-selecting logic because ambiguous.
To properly handle multiple criteria that can be both set and un-set you should re-evaluate all criteria. Conceptually however you should be evaluating the rows to see if they match all of the criteria, rather then evaluating the individual cells and then selecting back to the row.
In javascript we can directly access the rows collection on a table, so we can easily iterate the rows and then compare the values of cells. In this case we only need the row value to match 1 of the options in each set, it don’t try to match all of the selected options in each option set:
I sligtly changed the HTML and redid the JavaScript as I saw that the logic didn’t work.
Here’s my working example:
EDIT NOTE: This code allows you to change your HTML table/filters without the need to adjust the JavaScript. You just need to place matching
data-filter
attributes at both the<input>
and<td>
that you want filtered.