In my article today, I will show you how to search through a table using a search form in JavaScript.
Assuming you have a horizontal table that lists out the contact info of all employees, and you need to build a search form to search through the table to find a particular employee, how would you go on about this?
For demo purposes, this is my table.
Now you may have a larger table than the image above, and our solution will still work just fine.
LET'S GET STARTED
In order to build our search algorithm, we need to first, define the columns we will be searching through.
For this tutorial, I have decided to make the algorithm search for the presence of the search text in the name, email, phone, and country columns.
For brevity's sake, here's the HTML and CSS, as we want to focus on the JavaScript aspect.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
html{
font-family: "Roboto", sans-serif;
width:100%;
}
.mw-500{
max-width:500px;
}
.table {
margin: 0 auto;
text-align: center;
border-collapse: collapse;
border: 1px solid #d4d4d4;
background: #fff;
border: 1px solid #dee2e6;
width:100%;
max-width:inherit;
caption-side:bottom;
border-collapse:collapse
}
.table th,
.table td {
padding: 10px 10px;
width:50%;
}
.table>tbody {
vertical-align:inherit
}
.table>thead {
vertical-align:bottom
}
.table th td, .table tr td{
width:auto;
}
.table {
font-weight: 700;
}
.table th {
border-bottom: 1px solid #d4d4d4;
}
.table td,
th {
border-bottom-width: 2px;
border: 1px solid #dee2e6;
}
#inp_search{
margin-bottom: 20px;
outline: 0;
height: 2rem;
width: 100%;
}
</style>
</head>
<body>
<div align="center">
<div class="mw-500">
<input type="search" id="inp_search">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Email</td>
<td>Phone</td>
<td>Country</td>
</tr>
</thead>
<tbody id="append_data">
<tr>
<td colspan="5">NO DATA FOUND</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>
Here's what the code looks like
Now we want to declare an object that will store the table's data and a function that will build the table for us using the data already declared.
//our table's data
const tableData = {
1 : ["Simon ugorji", "simon@gmail.com", "01234", "Germany"],
2 : ["Tony ugorji", "tony@gmail.com", "013234", "Turkey"],
3 : ["Victor ugorji", "victor@gmail.com", "014234", "Germany"],
4 : ["Felix ugorji", "felix@gmail.com", "011234", "Canada"],
5 : ["Jordan ugorji", "jordan@gmail.com", "016234", "Costa Rica"],
6 : ["Henry ugorji", "henry@gmail.com", "0166234", "Belgium"],
7 : ["Frank Sams", "simon@gmail.com", "01234", "Nigeria"],
8 : ["John Doe", "jonny@gmail.com", "0123466", "Australia"],
9 : ["Peter Lamb", "peter@gmail.com", "0123774", "Algeria"],
10 : ["Rodney Human", "human@gmail.com", "0128934", "Ukraine"]
}
THE FUNCTION
The function below will have 1 argument (I'll talk about the argument later in this tutorial), and the purpose of the function is to generate the records (rows and cells).
//store table data element
const tableDataElem = document.querySelector('#append_data');
//function to build table
function buildTable(matchingIDs){
//reset table data
tableDataElem.innerHTML = "";
//check if parameter is provided
if(typeof matchingIDs === "undefined"){
//our loop index
let ind = 0;
//our table index
let tableInd = 1;
//loop through table object
while(ind < Object.keys(tableData).length){
//append data using a template literal
tableDataElem.innerHTML += `
<tr>
<td>${tableInd}</td>
<td>${tableData[tableInd][0]}</td>
<td>${tableData[tableInd][1]}</td>
<td>${tableData[tableInd][2]}</td>
<td>${tableData[tableInd][3]}</td>
</tr>
`;
//increment loop index
ind++;
//increment table index
tableInd++;
}
}
}
If I invoke the function, you will notice that our table now has all employee data.
//our table's data
const tableData = {
1 : ["Simon ugorji", "simon@gmail.com", "01234", "Germany"],
2 : ["Tony ugorji", "tony@gmail.com", "013234", "Turkey"],
3 : ["Victor ugorji", "victor@gmail.com", "014234", "Paris"],
4 : ["Felix ugorji", "felix@gmail.com", "011234", "Canada"],
5 : ["Jordan ugorji", "jordan@gmail.com", "016234", "Costa Rica"],
6 : ["Henry ugorji", "henry@gmail.com", "0166234", "Belgium"],
7 : ["Frank Sams", "simon@gmail.com", "01234", "Nigeria"],
8 : ["John Doe", "jonny@gmail.com", "0123466", "Australia"],
9 : ["Peter Lamb", "peter@gmail.com", "0123774", "Algeria"],
10 : ["Rodney Human", "human@gmail.com", "0128934", "Ukraine"]
}
//invoke the function
buildTable();
Here's what it looks like
BUILDING THE SEARCH ALGORITHM
Now we want to build a very simple algorithm that will search through the data used to build the table for a specific query, then it will return the records that contain the searched query.
To build this logic, we will attach an event listener to the search input, then we will check if a value is provided as the search query.
I want to search and display records on the table as soon as I begin typing. So I will use the input event listener, then check if the search query is empty or not.
If the search query is not empty, it means that the user provided a value, and we will search through the table, but if the search query is empty, it means that the user did not provide a value, then we will rebuild the table using the data already declared.
//attach event listener
document.querySelector('#inp_search').addEventListener('input', function(){
//store the search query
let value = this.value.trim();
//check if value is not empty
if(value){
}else{
}
})
ITS TIME FOR AN EXPLANATION
Recall that we declared the buildTable function to have just one parameter?
Well, here's the use.
All records in the table have an ID attached to each of them. So when we search through the table, we will store the ID of the records that contain the matching text of the search query, then pass in those IDs to the buildTable function so that it will display the records that bear such ID.
Let us modify the buildTable function to handle the parameter if it is provided.
//function to build table
function buildTable(matchingIDs){
//reset table data
tableDataElem.innerHTML = "";
//check if parameter is provided
if(typeof matchingIDs === "undefined"){
//our loop index
let ind = 0;
//our table index
let tableInd = 1;
//loop through table object
while(ind < Object.keys(tableData).length){
//append data using a template literal
tableDataElem.innerHTML += `
<tr>
<td>${tableInd}</td>
<td>${tableData[tableInd][0]}</td>
<td>${tableData[tableInd][1]}</td>
<td>${tableData[tableInd][2]}</td>
<td>${tableData[tableInd][3]}</td>
</tr>
`;
//increment loop index
ind++;
//increment table index
tableInd++;
}
}else if(matchingIDs.length !== 0){ //check if matchingIDs array is not empty
//our loop index
let ind = 0;
//our table index
let tableInd = 1;
//loop through matching IDs provided
while(ind < matchingIDs.length){
//append data by getting ID of the record and using a template literal
tableDataElem.innerHTML+=`
<tr>
<td>${tableInd}</td>
<td>${tableData[matchingIDs[ind]][0]}</td>
<td>${tableData[matchingIDs[ind]][1]}</td>
<td>${tableData[matchingIDs[ind]][2]}</td>
<td>${tableData[matchingIDs[ind]][3]}</td>
</tr>
`;
//increment loop index
ind++;
//increment table index
tableInd++;
}
}else{
tableDataElem.innerHTML+=`
<tr>
<td colspan="5">
NO DATA FOUND
</td>
</tr>
`;
}
}
Now the function has been modified to rebuild the table to display:
- All Records if the matchingIDs parameter is not provided.
- All Records that contain the search query if the matchingIDs parameter is provided.
- "NO DATA FOUND" If the search query returned no results
Now let us complete our code to handle the search query of the user.
//attach event listener
document.querySelector('#inp_search').addEventListener('input', function(){
//store the search query
let value = this.value.trim();
//check if value is not empty
if(value){
//store matching record IDs
let matchingIDs = [];
//loop index
let ind = 1;
//loop through the data to find matching text
while(ind < Object.keys(tableData).length){
//check if current property contains the search query
if(tableData[ind][0].includes(value) ||
tableData[ind][1].includes(value) ||
tableData[ind][2].includes(value) ||
tableData[ind][3].includes(value)){
//store the id of the record
matchingIDs.push(ind);
}
//increment index
ind++;
}
//invoke the function by passing in the matching IDs
buildTable(matchingIDs);
}else{
//invoke the build table function without providing an argument
buildTable();
}
})
You will notice that I used the includes() method to check if the query exists within a particular property. Now we can boldly test our algorithm.
Copy and paste the full code below to your page and search for an employee to see the magic
//our table's data
const tableData = {
1 : ["Simon ugorji", "simon@gmail.com", "01234", "Germany"],
2 : ["Tony ugorji", "tony@gmail.com", "013234", "Turkey"],
3 : ["Victor ugorji", "victor@gmail.com", "014234", "Germany"],
4 : ["Felix ugorji", "felix@gmail.com", "011234", "Canada"],
5 : ["Jordan ugorji", "jordan@gmail.com", "016234", "Costa Rica"],
6 : ["Henry ugorji", "henry@gmail.com", "0166234", "Belgium"],
7 : ["Frank Sams", "sams@gmail.com", "01234", "Nigeria"],
8 : ["John Doe", "jonny@gmail.com", "0123466", "Australia"],
9 : ["Peter Lamb", "peter@gmail.com", "0123774", "Algeria"],
10 : ["Rodney Human", "human@gmail.com", "0128934", "Ukraine"]
}
//store table data element
const tableDataElem = document.querySelector('#append_data');
//function to build table
function buildTable(matchingIDs){
//reset table data
tableDataElem.innerHTML = "";
//check if parameter is provided
if(typeof matchingIDs === "undefined"){
//our loop index
let ind = 0;
//our table index
let tableInd = 1;
//loop through table object
while(ind < Object.keys(tableData).length){
//append data using a template literal
tableDataElem.innerHTML += `
<tr>
<td>${tableInd}</td>
<td>${tableData[tableInd][0]}</td>
<td>${tableData[tableInd][1]}</td>
<td>${tableData[tableInd][2]}</td>
<td>${tableData[tableInd][3]}</td>
</tr>
`;
//increment loop index
ind++;
//increment table index
tableInd++;
}
}else if(matchingIDs.length !== 0){ //check if matchingIDs array is not empty
//our loop index
let ind = 0;
//our table index
let tableInd = 1;
//loop through matching IDs provided
while(ind < matchingIDs.length){
//append data by getting ID of the record and using a template literal
tableDataElem.innerHTML+=`
<tr>
<td>${tableInd}</td>
<td>${tableData[matchingIDs[ind]][0]}</td>
<td>${tableData[matchingIDs[ind]][1]}</td>
<td>${tableData[matchingIDs[ind]][2]}</td>
<td>${tableData[matchingIDs[ind]][3]}</td>
</tr>
`;
//increment loop index
ind++;
//increment table index
tableInd++;
}
}else{
tableDataElem.innerHTML+=`
<tr>
<td colspan="5">
NO DATA FOUND
</td>
</tr>
`;
}
};
buildTable();
//attach event listener
document.querySelector('#inp_search').addEventListener('input', function(){
//store the search query
let value = this.value.trim();
//check if value is not empty
if(value){
//store matching record IDs
let matchingIDs = [];
//loop index
let ind = 1;
//loop through the data to find matching text
while(ind < Object.keys(tableData).length){
//check if current property contains the search query
if(tableData[ind][0].includes(value) ||
tableData[ind][1].includes(value) ||
tableData[ind][2].includes(value) ||
tableData[ind][3].includes(value)){
//store the id of the record
matchingIDs.push(ind);
}
//increment index
ind++;
}
//invoke the function by passing in the matching IDs
buildTable(matchingIDs);
}else{
//invoke the build table function without providing an argument
buildTable();
}
})
Here's what the table looks like when I enter the query "ugorji"
Here's what the table looks like when I enter the query "sams"
Here's what the table looks like when I enter the query "samuel" which doesn't exist
Recall that we built the search algorithm to also look for the presence of a search query in the country column?
Here's what the table looks like when I enter the query "Germany"
Here's what the table looks like if we didn't provide any search query
I hope you've understood one of the ways to build a search form to be able to search through a table in JavaScript.
UPDATE
Note that using the method includes() on a string performs a case-sensitive search, which is not what we need. To perform a case-insensitive search on a string, we need to use a regular expression.
In this article, I explained how to construct a regular expression to perform a case-insensitive search on a string.
Having read the article, here’s a modification to the code.
//attach event listener
document.querySelector('#inp_search').addEventListener('input', function(){
//store the search query
let value = this.value.trim();
//check if value is not empty
if(value){
//store matching record IDs
let matchingIDs = [];
//loop index
let ind = 1;
//loop through the data to find matching text
while(ind < Object.keys(tableData).length){
//perform case-insensitive search on the string
if(tableData[ind][0].match(new RegExp(value, 'i')) ||
tableData[ind][1].match(new RegExp(value, 'i')) ||
tableData[ind][2].match(new RegExp(value, 'i')) ||
tableData[ind][3].match(new RegExp(value, 'i'))){
//store the id of the record
matchingIDs.push(ind);
}
//increment index
ind++;
}
//invoke the function by passing in the matching IDs
buildTable(matchingIDs);
}else{
//invoke the build table function without providing an argument
buildTable();
}
})
Now if you search for “frank”, It will pull up the customer’s name without the need to use an uppercase letter.
Here's the code on codepen
Do you have any suggestions? Let me know in the comment section!
You've reached the end of my article.
EXTRA
I recently launched a JavaScript package that validates your HTML forms using validation rules, regular expressions, and form input attributes.
I will appreciate it if you spared a few seconds to check it out.
Thank You
Top comments (3)
In a competition I had 10 minutes to solve this problem....
I also think it's not such a good idea to hardcode the number of columns...
I love this solution, Frank!
I'm still trying to wrap my head around the one-liner 😅😅😅.
Thanks for your contribution, Frank!
I will add this code to my collection.
😁