我有一个表,需要根据多列进行过滤,我的示例使用复选框来过滤 5 列,某些列是隐藏的,不会显示在表中,但仍然可以进行过滤。
我需要它如何工作的示例:
如果没有选择过滤器,则默认显示所有行,目前表格默认为空,需要选中复选框才能显示行。
如果选中“月份”过滤器“MAR”,则会显示满足该条件的 4 行,然后我添加语言过滤器“法语”和“西班牙语”,并且只会显示满足两个过滤器条件的 3 行。然后,我取消选中“语言”过滤器并检查“房屋”过滤器“无”,仅显示满足新条件的 1 行。
我当前的尝试存在一些问题,例如过滤器类别未正确地与在一起,因此当选择“Ron”和“Mar”时,结果包括与“Ron”不匹配的其他名称。 另一个问题是,当清除过滤器时,由于先前的过滤器而隐藏的许多行不会变得可见。
可用示例在这里: 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>
当前逻辑的问题在于,您仅针对刚刚更改的过滤器输入显式地根据每个输入评估列。
您的规则只需要匹配所选选项中的一个,如果行包含不匹配的单元格,您的代码将隐藏行,因此,如果选择
JAN
,则包含 FEB
和 MAR
的行以及 JAN
都会被过滤掉。
当前取消选择过滤器的代码不会验证其他列条件,因此取消选择逻辑是因为不明确。
要正确处理可以设置和取消设置的多个条件,您应该重新评估所有条件。但从概念上讲,您应该评估行以查看它们是否符合所有条件,而不是评估单个单元格,然后选择返回该行。
在javascript中,我们可以直接访问表格上的行集合,因此我们可以轻松地迭代行,然后比较单元格的值。在这种情况下,我们只需要行值匹配每个选项集中的 1 个选项,它不会尝试匹配每个选项集中的所有选定选项:
function applyFilters() {
let option1 = getFilter(`input[type='checkbox'].option1`);
let option2 = getFilter(`input[type='checkbox'].option2`);
let option3 = getFilter(`input[type='checkbox'].option3`);
let option4 = getFilter(`input[type='checkbox'].option4`);
let rows = Array.from(document.querySelector('table.myTable').rows);
rows.forEach(row => {
// skip the header row
if (row.getAttribute('class') === 'header') return;
let visible = checkRowFilter(row, '.check1', option1)
&& checkRowFilter(row, '.check2', option2)
&& checkRowFilter(row, '.check3', option3)
&& checkRowFilter(row, '.check4', option4);
if(visible)
row.style = "";
else
row.style = "display:none";
});
}
/** Get the selected options
* @param {string} filterQuery - The query selector expression for the checkboxes to evaluate.
* @returns {Array} of the values for the selected checkboxes
*/
function getFilter(filterQuery) {
let options = Array.from(document.querySelectorAll(filterQuery));
return options.filter(option => option.checked == true)
.map(option => option.value.toLowerCase());
}
/** Evaluate the columns matching the filterCol against the options
* @param {row} row - The table row to evaluate.
* @param {string} filterQuery - The query selector expression for the columns to evaluate.
* @param {Array} options - The array of string values that are valid.
* @returns {boolean} True if one of the cells matches one of the options
*/
function checkRowFilter(row, filterQuery, options) {
if (options.length > 0){
let cells = Array.from(row.querySelectorAll(filterQuery));
//let values = cells.filter(c => c.innerHTML.trim().length > 0).length;
let matches = cells.filter(c => options.includes(c.innerHTML.toLowerCase())).length;
return matches > 0;
}
return true;
}
document.querySelectorAll('.option1')
.forEach(input => input.addEventListener('input', applyFilters));
document.querySelectorAll('.option2')
.forEach(input => input.addEventListener('input', applyFilters));
document.querySelectorAll('.option3')
.forEach(input => input.addEventListener('input', applyFilters));
document.querySelectorAll('.option4')
.forEach(input => input.addEventListener('input', applyFilters));
这是第一个原理方法,虽然它可以发挥作用,但更常见的设计将涉及过滤数据,然后重新渲染数据。这方面的例子超出了本文的范围,但值得您在成为前端开发人员的过程中学习。