Reputation: 353
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 completely 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.
Upvotes: 0
Views: 12987
Reputation: 1
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
Upvotes: 0
Reputation: 2730
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:
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>
Upvotes: 1
Reputation: 3820
No 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 comment)
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>
Upvotes: 6
Reputation: 346
You can combine 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";
}
}
}
}
Upvotes: 1