DEV Community

Cover image for How To Search Through a Table with JavaScript
Simon Ugorji
Simon Ugorji

Posted on • Edited on

How To Search Through a Table with JavaScript

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.

my demo 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>
Enter fullscreen mode Exit fullscreen mode

Here's what the code looks like

image.png

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"]
}
Enter fullscreen mode Exit fullscreen mode

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++;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Here's what it looks like

image.png

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{

    }
})
Enter fullscreen mode Exit fullscreen mode

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>
        `;
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
    }
})
Enter fullscreen mode Exit fullscreen mode

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();
    }
})
Enter fullscreen mode Exit fullscreen mode

Here's what the table looks like when I enter the query "ugorji"

table-3

Here's what the table looks like when I enter the query "sams"

table-4.PNG

Here's what the table looks like when I enter the query "samuel" which doesn't exist

table-6.PNG

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"

image.png

Here's what the table looks like if we didn't provide any search query

table-7.PNG

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();
    }
})
Enter fullscreen mode Exit fullscreen mode

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.

octaValidate

Thank You

Top comments (3)

Collapse
 
frankwisniewski profile image
Frank Wisniewski • Edited

In a competition I had 10 minutes to solve this problem....

<!DOCTYPE html>
<html lang=de>
  <meta charset=UTF-8>
  <title>Document</title>
  <link rel=stylesheet 
    href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
<body class=container>
  <h1>filter table</h1>
  <input type=search id=F>
  <table>
    <thead>
      <tr><td>City<td>Einwohner
      <tbody id=EW>
        <tr><td>Berlin<td>3748148
        <tr><td>Hamburg<td>1891810
        <tr><td>München<td>1471508
        <tr><td>Köln<td>1085664
        <tr><td>Frankfurt am Main<td>753056
        <tr><td>Stuttgart<td>634830
        <tr><td>Düsseldorf<td>619294
        <tr><td>Leipzig<td>587854
  </table>
    <script>
      F.onkeyup=e=>[...EW.rows].map(e=> e.style
       .display=[...e.cells]
        .map( e => e.innerText)
        .join(';').match(RegExp(F.value,'i'))?'':'none');  
    </script>
Enter fullscreen mode Exit fullscreen mode

I also think it's not such a good idea to hardcode the number of columns...

Collapse
 
ugorji_simon profile image
Simon Ugorji

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.

Collapse
 
frankwisniewski profile image
Frank Wisniewski

😁