admin管理员组文章数量:1291002
I have a HTML table having three columns - (Name, Age, City). I'm trying to achieve 'MS Excel' like functionality, where I can filter multiple columns.
Although the filters are working individually, they malfunction when the user enters text in multiple input fields at once. For example, just entering the name would work fine, but entering the name along with the city, will pletely rule out the name filter.
function nameSearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_name = tr[i].getElementsByTagName("td")[0];
if (td_name) {
txtValue_name = td_name.textContent || td_name.innerText;
if (txtValue_name.toUpperCase().indexOf(filter_name) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function ageSearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_age = tr[i].getElementsByTagName("td")[1];
if (td_age) {
txtValue_age = td_age.textContent || td_age.innerText;
if (txtValue_age.toUpperCase().indexOf(filter_age) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function citySearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_city = tr[i].getElementsByTagName("td")[2];
if (td_city) {
txtValue_city = td_city.textContent || td_city.innerText;
if (txtValue_city.toUpperCase().indexOf(filter_city) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
table,
td,
th {
border: 1px solid black;
border-collapse: collapse;
padding: 10px;
margin-top: 20px;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<input type="text" id="name-search" onkeyup="nameSearch()" placeholder="Name.." class="table-search-filters">
<input type="text" id="age-search" onkeyup="ageSearch()" placeholder="Age.." class="table-search-filters">
<input type="text" id="city-search" onkeyup="citySearch()" placeholder="City.." class="table-search-filters">
<table id="custom-table">
<thead>
<th>Name</th>
<th>Age</th>
<th>City</th>
</thead>
<tbody>
<tr>
<td>Bruce</td>
<td>32</td>
<td>Gotham</td>
</tr>
<tr>
<td>Bane</td>
<td>32</td>
<td>Chicago</td>
</tr>
<tr>
<td>Joker</td>
<td>28</td>
<td>Gotham</td>
</tr>
<tr>
<td>Harvey</td>
<td>30</td>
<td>Miami</td>
</tr>
</tbody>
</table>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
I have a HTML table having three columns - (Name, Age, City). I'm trying to achieve 'MS Excel' like functionality, where I can filter multiple columns.
Although the filters are working individually, they malfunction when the user enters text in multiple input fields at once. For example, just entering the name would work fine, but entering the name along with the city, will pletely rule out the name filter.
function nameSearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_name = tr[i].getElementsByTagName("td")[0];
if (td_name) {
txtValue_name = td_name.textContent || td_name.innerText;
if (txtValue_name.toUpperCase().indexOf(filter_name) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function ageSearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_age = tr[i].getElementsByTagName("td")[1];
if (td_age) {
txtValue_age = td_age.textContent || td_age.innerText;
if (txtValue_age.toUpperCase().indexOf(filter_age) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
function citySearch() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_city = tr[i].getElementsByTagName("td")[2];
if (td_city) {
txtValue_city = td_city.textContent || td_city.innerText;
if (txtValue_city.toUpperCase().indexOf(filter_city) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
table,
td,
th {
border: 1px solid black;
border-collapse: collapse;
padding: 10px;
margin-top: 20px;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<input type="text" id="name-search" onkeyup="nameSearch()" placeholder="Name.." class="table-search-filters">
<input type="text" id="age-search" onkeyup="ageSearch()" placeholder="Age.." class="table-search-filters">
<input type="text" id="city-search" onkeyup="citySearch()" placeholder="City.." class="table-search-filters">
<table id="custom-table">
<thead>
<th>Name</th>
<th>Age</th>
<th>City</th>
</thead>
<tbody>
<tr>
<td>Bruce</td>
<td>32</td>
<td>Gotham</td>
</tr>
<tr>
<td>Bane</td>
<td>32</td>
<td>Chicago</td>
</tr>
<tr>
<td>Joker</td>
<td>28</td>
<td>Gotham</td>
</tr>
<tr>
<td>Harvey</td>
<td>30</td>
<td>Miami</td>
</tr>
</tbody>
</table>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
Instead of having three seperate 'onkeyup' function, I also tried to map all the inputs to a single function, but that still didn't help much.
Share Improve this question edited Apr 13, 2020 at 14:22 Rohit Girdhar asked Apr 13, 2020 at 14:00 Rohit GirdharRohit Girdhar 3534 gold badges9 silver badges26 bronze badges 7- Well, I think, you need a kinda source values list and visible values list. The source one is an input to your main filter function which returns a subset of the list (visible values). You should always apply all filters to your source list. Keyup event listeners should only change params for that filters. – Ivan Burnaev Commented Apr 13, 2020 at 14:06
- Can you please elaborate on what do you mean by source values list? – Rohit Girdhar Commented Apr 13, 2020 at 14:23
-
FYI: instead of
onkeyup
you probably want to useoninput
. As it is now if I copy and paste text using the mouse it won't filter since no keys were pressed. – user128511 Commented Apr 13, 2020 at 14:29 - Does this answer your question? Filtering table multiple columns – Heretic Monkey Commented Apr 13, 2020 at 14:36
- @HereticMonkey Unfortunately it doesn't because, that answer is using one input filter across multiple columns whereas I'm looking for multiple column filter using several input fields. – Rohit Girdhar Commented Apr 13, 2020 at 14:42
4 Answers
Reset to default 6No need of separate onKeyUp handlers for each input.Just one handler is enough.
Instead of getting elements by tag "td" tr[i].getElementsByTagName("td")
, use tr[i].cells
and table.rows
to get rows (From gman's ment)
instead of tr =table.getElementsByTagName("tr");
tr = table.rows;
for (let i = 0; i < tr.length; i++) {
td= tr[i].cells;
td_name =td[0].innerText;
td_age = td[1].innerText;
td_city = td[2].innerText;
if (td_name.toUpperCase().indexOf(filter_name) > -1 && td_age.toUpperCase().indexOf(filter_age) > -1 && td_city.toUpperCase().indexOf(filter_city) > -1) {
tr[i].style.display = "";
}
else
tr[i].style.display = "none";
}
var input_name = document.getElementById("name-search");
var input_age = document.getElementById("age-search");
var input_city = document.getElementById("city-search");
var table = document.getElementById("custom-table");
function search() {
let filter_name = input_name.value.toUpperCase();
let filter_age = input_age.value.toUpperCase();
let filter_city = input_city.value.toUpperCase();
let tr = table.rows;
for (let i = 0; i < tr.length; i++) {
td = tr[i].cells;
td_name = td[0].innerText;
td_age = td[1].innerText;
td_city = td[2].innerText;
if (td_name.toUpperCase().indexOf(filter_name) > -1 && td_age.toUpperCase().indexOf(filter_age) > -1 && td_city.toUpperCase().indexOf(filter_city) > -1) {
tr[i].style.display = "";
} else
tr[i].style.display = "none";
}
}
table,
td,
th {
border: 1px solid black;
border-collapse: collapse;
padding: 10px;
margin-top: 20px;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<input type="text" id="name-search" onkeyup="search()" placeholder="Name.." class="table-search-filters">
<input type="text" id="age-search" onkeyup="search()" placeholder="Age.." class="table-search-filters">
<input type="text" id="city-search" onkeyup="search()" placeholder="City.." class="table-search-filters">
<table id="custom-table">
<thead>
<th>Name</th>
<th>Age</th>
<th>City</th>
</thead>
<tbody>
<tr>
<td>Bruce</td>
<td>32</td>
<td>Gotham</td>
</tr>
<tr>
<td>Bane</td>
<td>32</td>
<td>Chicago</td>
</tr>
<tr>
<td>Joker</td>
<td>28</td>
<td>Gotham</td>
</tr>
<tr>
<td>Harvey</td>
<td>30</td>
<td>Miami</td>
</tr>
</tbody>
</table>
</body>
</html>
You can bine your three function into one and just check conditions with and(&&). hope below code helps.
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<input type="text" id="name-search" onkeyup="search()" placeholder="Name.." class="table-search-filters">
<input type="text" id="age-search" onkeyup="search()" placeholder="Age.." class="table-search-filters">
<input type="text" id="city-search" onkeyup="search()" placeholder="City.." class="table-search-filters">
<table id="custom-table">
<thead>
<th>Name</th>
<th>Age</th>
<th>City</th>
</thead>
<tbody>
<tr>
<td>Bruce</td>
<td>32</td>
<td>Gotham</td>
</tr>
<tr>
<td>Bane</td>
<td>32</td>
<td>Chicago</td>
</tr>
<tr>
<td>Joker</td>
<td>28</td>
<td>Gotham</td>
</tr>
<tr>
<td>Harvey</td>
<td>30</td>
<td>Miami</td>
</tr>
</tbody>
</table>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
script.js
function search() {
var input_name, input_age, input_city, filter, table, tr, td, i, txtValue_name, txtValue_age, txtValue_city;
input_name = document.getElementById("name-search");
input_age = document.getElementById("age-search");
input_city = document.getElementById("city-search");
filter_name = input_name.value.toUpperCase();
filter_age = input_age.value.toUpperCase();
filter_city = input_city.value.toUpperCase();
table = document.getElementById("custom-table");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td_city = tr[i].getElementsByTagName("td")[2];
td_age = tr[i].getElementsByTagName("td")[1];
td_name = tr[i].getElementsByTagName("td")[0];
if(td_city && td_age && td_name){
txtValue_city = td_city.textContent || td_city.innerText;
txtValue_age = td_age.textContent || td_age.innerText;
txtValue_name = td_name.textContent || td_name.innerText;
if (txtValue_city.toUpperCase().indexOf(filter_city) > -1
&& txtValue_age.toUpperCase().indexOf(filter_age) > -1
&& txtValue_name.toUpperCase().indexOf(filter_name) > -1) {
tr[i].style.display = "";
}
else {
tr[i].style.display = "none";
}
}
}
}
So, you need to create a filtering function for each filter. You can use startsWith
, includes
or ===
to achieve a different searching behaviours.
Next, you need to create a "main" filter which will call all other filters.
Then add an event listener to the parent element (in my snipped I added it to the window
object) to prevent multiple event listeners. When event occurs check it's target and call main filter function it it's needed.
Some obvious features:
- custom filtering behavious
- pure functions which can be easily tested
- posable main filter function
- no imperative mess =) (debatable)
const sourceList = Array.from(document.querySelectorAll("tbody tr"));
const nameFilter = (value, item) => !value || item.querySelector("td:nth-child(1)").textContent.toLowerCase().includes(value.toLowerCase());
const ageFilter = (value, item) => !value || item.querySelector("td:nth-child(2)").textContent.startsWith(value);
const cityFilter = (value, item) => !value || item.querySelector("td:nth-child(3)").textContent.toLowerCase().includes(value.toLowerCase());
const mainFilter = ({name, age, city}, item) => {
return nameFilter(name, item) && ageFilter(age, item) && cityFilter(city, item);
}
const currentFilters = {
name: '',
age: '',
city: '',
};
window.addEventListener('input', event => {
if (event.target.matches('.table-search-filters')) {
currentFilters[event.target.name] = event.target.value;
sourceList.forEach(item => {
const isVisible = mainFilter(currentFilters, item);
item.style.display = !isVisible ? 'none' : 'inherit';
})
}
})
const table = document.querySelector('table');
<input name="name" type="text" id="name-search" placeholder="Name.." class="table-search-filters">
<input name="age" type="text" id="age-search" placeholder="Age.." class="table-search-filters">
<input name="city" type="text" id="city-search" placeholder="City.." class="table-search-filters">
<table id="custom-table">
<thead>
<th>Name</th>
<th>Age</th>
<th>City</th>
</thead>
<tbody>
<tr>
<td>Bruce</td>
<td>32</td>
<td>Gotham</td>
</tr>
<tr>
<td>Bane</td>
<td>32</td>
<td>Chicago</td>
</tr>
<tr>
<td>Joker</td>
<td>28</td>
<td>Gotham</td>
</tr>
<tr>
<td>Harvey</td>
<td>30</td>
<td>Miami</td>
</tr>
</tbody>
</table>
If you want to include a header change to include class = header or change to
<tr class="header">
<th>Name</th>
<th>Age</th>
<th>City</th>
</tr>
inside
and use this line of code in *.js filetr = table.querySelectorAll("tbody tr:not(.header)");
table header missing after search
本文标签: javascriptHTML Table Multiple Column FiltersStack Overflow
版权声明:本文标题:javascript - HTML Table Multiple Column Filters - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741524338a2383377.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论