0

Task

I have a table with multiple columns. Each column has an input field above it. I want to filter each row such that the table shows any rows that have at least one column that contains the value in the input field above the column.

Issue

When I try typing in values into the input fields, the table does not filter. I am not sure where my error is. Since I am still very much in the learning phase, any tips for syntax or performance would also be appreciated. The following minimally (not-yet) working example is the best I could do based on various answers on this website.

Code

function performReset() {
  document.getElementById("inputName").value = "";
  document.getElementById("inputCity").value = "";
  document.getElementById("inputCountry").value = "";
  filterTable();
}

function filterTable() {
  // Get the table rows.
  let rows = document.querySelector("#myTable > tbody").rows;

  // Get the inputed values to filter against.
  let inputedName = document.getElementById("inputName").value.toUpperCase();
  let inputedCity = document.getElementById("inputCity").value.toUpperCase();
  let inputedCountry = document.getElementById("inputCountry").value.toUpperCase();

  // Loop over the rows to perform the filter.
  let totalRows = rows.length;
  for (let i = 0; i < totalRows; i++) {
    // Get the contents of the relevant cell data.
    let rowName = rows[i].cells[0].textContent.toUpperCase();
    let rowCity = rows[i].cells[1].textContent.toUpperCase();
    let rowCountry = rows[i].cells[2].textContent.toUpperCase();

    // Define an array of conditions.
    // Note: the conditions are evaluated here.
    let conditionsArray = [
      rowName.indexOf(inputedName) > -1,
      rowCity.indexOf(inputedCity) > -1,
      rowCountry.indexOf(inputedCountry) > -1
    ];

    // If any of the above conditions are true then show the row,
    // otherwise turn of displaying that row.
    if (conditionsArray.indexOf(true) > -1) {
      rows[i].display = "";
    } else {
      rows[i].display = "none";
    }
  }
}
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet" />
<h2 class="w3-center">Filter Table Test</h2>

<p class="w3-center"><button id="buttonReset" onclick="performReset()" class="w3-button w3-grey w3-hover-light-grey">Reset</button></p>

<table id="myTable" class="w3-table w3-striped w3-hoverable">
  <!--Inputs Used For Filtering-->
  <tr>
    <td><input id="inputName" onkeyup="filterTable()" class="w3-input" placeholder="Name..."></td>
    <td><input id="inputCity" onkeyup="filterTable()" class="w3-input" placeholder="City..."></td>
    <td><input id="inputCountry" onkeyup="filterTable()" class="w3-input" placeholder="Country..."></td>
  </tr>
  <!--Column Headings-->
  <tr class="w3-dark-grey">
    <th>Customer</th>
    <th>City</th>
    <th>Country</th>
  </tr>
  <!--Row Data-->
  <tbody>
    <tr class="item">
      <td>Alfreds Futterkiste</td>
      <td>Berlin</td>
      <td>Germany</td>
    </tr>
    <tr class="item">
      <td>Berglunds snabbköp</td>
      <td>Lule </td>
      <td>Sweden</td>
    </tr>
    <tr class="item">
      <td>Centro comercial Moctezuma</td>
      <td>México D.F.</td>
      <td>Mexico</td>
    </tr>
    <tr class="item">
      <td>Ernst Handel</td>
      <td>Graz</td>
      <td>Austria</td>
    </tr>
    <tr class="item">
      <td>FISSA Fabrica Inter. Salchichas S.A.</td>
      <td>Madrid</td>
      <td>Spain</td>
    </tr>
    <tr class="item">
      <td>Galería del gastrónomo</td>
      <td>Barcelona</td>
      <td>Spain</td>
    </tr>
    <tr class="item">
      <td>Island Trading</td>
      <td>Cowes</td>
      <td>UK</td>
    </tr>
    <tr class="item">
      <td>Königlich Essen</td>
      <td>Brandenburg</td>
      <td>Germany</td>
    </tr>
    <tr class="item">
      <td>Laughing Bacchus Wine Cellars</td>
      <td>Vancouver</td>
      <td>Canada</td>
    </tr>
    <tr class="item">
      <td>Magazzini Alimentari Riuniti</td>
      <td>Bergamo</td>
      <td>Italy</td>
    </tr>
    <tr class="item">
      <td>North/South</td>
      <td>London</td>
      <td>UK</td>
    </tr>
    <tr class="item">
      <td>Paris spécialités</td>
      <td>Paris</td>
      <td>France</td>
    </tr>
    <tr class="item">
      <td>Rattlesnake Canyon Grocery</td>
      <td>Albuquerque</td>
      <td>USA</td>
    </tr>
    <tr class="item">
      <td>Simons bistro</td>
      <td>København</td>
      <td>Denmark</td>
    </tr>
    <tr class="item">
      <td>The Big Cheese</td>
      <td>Portland</td>
      <td>USA</td>
    </tr>
    <tr class="item">
      <td>Vaffeljernet</td>
      <td>Århus</td>
      <td>Denmark</td>
    </tr>
    <tr class="item">
      <td>Wolski Zajazd</td>
      <td>Warszawa</td>
      <td>Poland</td>
    </tr>
  </tbody>
</table>

Edit 1: Modified document.querySelector('#myTable tbody') to document.querySelector('#myTable > tbody'). But my issue persists.

2
  • Your table structure is causing the problem. Can you please change your querySelector to use: "#myTable > tbody" note the greater than sign. Currently, the selector is only selecting the two TRs that are prior to the tbody. Commented Nov 18, 2018 at 3:50
  • Thanks. I woke up thinking the samething, however, even with this change, my issue persists. Commented Nov 18, 2018 at 16:59

2 Answers 2

3

You can attach the filter function to the event of all the input elements by passing the event and specific index.

Try the following way:

function performReset() {
  document.getElementById("inputName").value = "";
  document.getElementById("inputCity").value = "";
  document.getElementById("inputCountry").value = "";
  filterTable(event, 0);
}

function filterTable(event, index) {
  var filter = event.target.value.toUpperCase();
  var rows = document.querySelector("#myTable tbody").rows;
  for (var i = 0; i < rows.length; i++) {
    var firstCol = rows[i].cells[0].textContent.toUpperCase();
    var secondCol = rows[i].cells[1].textContent.toUpperCase();
    var thirdCol = rows[i].cells[2].textContent.toUpperCase();
    if ((firstCol.indexOf(filter) > -1 && index == 0) || (secondCol.indexOf(filter) > -1 && index == 1) || (thirdCol.indexOf(filter) > -1 && index == 2)) {
      rows[i].style.display = "";
    } else {
      rows[i].style.display = "none";
    }      
  }
}

document.querySelectorAll('input.w3-input').forEach(function(el,idx){
  el.addEventListener('keyup', function(e){
    filterTable(e, idx);
  }, false);
});
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet" />
<h2 class="w3-center">Filter Table Test</h2>

<p class="w3-center"><button id="buttonReset" onclick="performReset()" class="w3-button w3-grey w3-hover-light-grey">Reset</button></p>

<table id="myTable" class="w3-table w3-striped w3-hoverable">
  <!--Column Headings-->
  <thead>
    <tr>
      <td><input id="inputName" class="w3-input" placeholder="Name..."></td>
      <td><input id="inputCity" class="w3-input" placeholder="City..."></td>
      <td><input id="inputCountry" class="w3-input" placeholder="Country..."></td>
    </tr>
  </thead>
  <!--Row Data-->
  <tbody>
    <tr class="item">
      <td>Alfreds Futterkiste</td>
      <td>Berlin</td>
      <td>Germany</td>
    </tr>
    <tr class="item">
      <td>Berglunds snabbköp</td>
      <td>Lule </td>
      <td>Sweden</td>
    </tr>
    <tr class="item">
      <td>Centro comercial Moctezuma</td>
      <td>México D.F.</td>
      <td>Mexico</td>
    </tr>
    <tr class="item">
      <td>Ernst Handel</td>
      <td>Graz</td>
      <td>Austria</td>
    </tr>
    <tr class="item">
      <td>FISSA Fabrica Inter. Salchichas S.A.</td>
      <td>Madrid</td>
      <td>Spain</td>
    </tr>
    <tr class="item">
      <td>Galería del gastrónomo</td>
      <td>Barcelona</td>
      <td>Spain</td>
    </tr>
    <tr class="item">
      <td>Island Trading</td>
      <td>Cowes</td>
      <td>UK</td>
    </tr>
    <tr class="item">
      <td>Königlich Essen</td>
      <td>Brandenburg</td>
      <td>Germany</td>
    </tr>
    <tr class="item">
      <td>Laughing Bacchus Wine Cellars</td>
      <td>Vancouver</td>
      <td>Canada</td>
    </tr>
    <tr class="item">
      <td>Magazzini Alimentari Riuniti</td>
      <td>Bergamo</td>
      <td>Italy</td>
    </tr>
    <tr class="item">
      <td>North/South</td>
      <td>London</td>
      <td>UK</td>
    </tr>
    <tr class="item">
      <td>Paris spécialités</td>
      <td>Paris</td>
      <td>France</td>
    </tr>
    <tr class="item">
      <td>Rattlesnake Canyon Grocery</td>
      <td>Albuquerque</td>
      <td>USA</td>
    </tr>
    <tr class="item">
      <td>Simons bistro</td>
      <td>København</td>
      <td>Denmark</td>
    </tr>
    <tr class="item">
      <td>The Big Cheese</td>
      <td>Portland</td>
      <td>USA</td>
    </tr>
    <tr class="item">
      <td>Vaffeljernet</td>
      <td>Århus</td>
      <td>Denmark</td>
    </tr>
    <tr class="item">
      <td>Wolski Zajazd</td>
      <td>Warszawa</td>
      <td>Poland</td>
    </tr>    
  </tbody>
</table>

Sign up to request clarification or add additional context in comments.

5 Comments

Running in Firefox, I get an error that event from filterTable(event, idx); inside the addEventListener function is undefined. Running in Chrome, I get no error, but instead the row of inputs and the row of headings disappear instead of filtering the rows below. I tried inserting event, index as parameters to the addEventListener anonymous function. With that change, Firefox no longer gives an error, but has the same behavior as Chrome; it disappears the row of inputs and row of headings instead of filtering the rows below.
Also, what is the difference between adding the event listeners at the end programmatically versus as attributes in the input tag itself?
@AliKakakhel, I have checked in Firefox (Version 63) and found no error....though I have updated some code now....you can check. For the second question, imagine you have lot of elements, you would need to add the function to all them, would not that be tedious and error prone. You also need to pass the index to the function based on which you have to set the condition......simply it is good practice to attach function in the code instead of HTML......thanks.
It is working in my Firefox 63 too. I must have typoed somehow or something before. Thank you for the lessons on html, js, and programming techniques. Looks like the major differences between my attempt and your code are as follows. 1. Programmatically adding event listeners. Like you said, this will be useful as I add more columns/inputs. 2. Checking the multiple conditions directly in the if statement rather than in a separate conditions array.
3. Removing the column headings row, instead, using the inputs as the headings. I wanted to enable the user to click on the headings to toggle sorting on that column. Not sure how to do that with the new arrangement. Maybe use <br>s to add <div>s underneath the inputs as click-sortable headings.
0

The @Mamun's answer is good and works. An other solution that i used is: i have used only one input tag for the three columns

you can try also this code

CSS:

<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet" />
<style>

* {
  box-sizing: border-box;
}

#myInput {
  background-image: url('/css/searchicon.png');
  background-position: 10px 10px;
  background-repeat: no-repeat;
  width: 100%;
  font-size: 16px;
  padding: 12px 20px 12px 40px;
  border: 1px solid #ddd;
  margin-bottom: 12px;
}

#myTable {
  border-collapse: collapse;
  width: 100%;
  border: 1px solid #ddd;
  font-size: 18px;
}

#myTable th, #myTable td {
  text-align: left;
  padding: 12px;
}

#myTable tr {
  border-bottom: 1px solid #ddd;
}

#myTable tr.header, #myTable tr:hover {
  background-color: #f1f1f1;
}
</style>
</head>

JAVASCRIPT:

<script>
  function performReset() {
  document.getElementById("myInput").value = "";
  filterTable();
}

function filterTable() {
  var input, filter, table, tr, i,name,country,city;
  input = document.getElementById("myInput");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");
  for (i = 0; i < tr.length; i++) {
    //write here which columns you want filter...
    name=tr[i].getElementsByTagName("td")[0];
    city=tr[i].getElementsByTagName("td")[1];
    country=tr[i].getElementsByTagName("td")[2];

    if (name) {
      if (name.innerHTML.toUpperCase().indexOf(filter) > -1 ||city.innerHTML.toUpperCase().indexOf(filter) > -1 || country.innerHTML.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }
  }
}
</script>

HTML:

<body>
<p class="w3-center"><button id="buttonReset" onclick="performReset()" class="w3-button w3-grey w3-hover-light-grey">Reset</button></p>

<h2>My Customers</h2>

<input type="text" id="myInput" onkeyup="filterTable()" placeholder="Search for names.." title="Type in a name">

<table id="myTable">
  <tr class="header">
    <th style="width:60%;">Name</th>
    <th style="width:20%;">City</th>
    <th style="width:20%;">Country</th>
  </tr>
  <tbody>
  <tr class="item">
    <td>Alfreds Futterkiste</td>
    <td>Berlin</td>
    <td>Germany</td>
  </tr>
  <tr class="item">
    <td>Berglunds snabbköp</td>
    <td>Lule </td>
    <td>Sweden</td>
  </tr>
  <tr class="item">
    <td>Centro comercial Moctezuma</td>
    <td>México D.F.</td>
    <td>Mexico</td>
  </tr>
  <tr class="item">
    <td>Ernst Handel</td>
    <td>Graz</td>
    <td>Austria</td>
  </tr>
  <tr class="item">
    <td>FISSA Fabrica Inter. Salchichas S.A.</td>
    <td>Madrid</td>
    <td>Spain</td>
  </tr>
  <tr class="item">
    <td>Galería del gastrónomo</td>
    <td>Barcelona</td>
    <td>Spain</td>
  </tr>
  <tr class="item">
    <td>Island Trading</td>
    <td>Cowes</td>
    <td>UK</td>
  </tr>
  <tr class="item">
    <td>Königlich Essen</td>
    <td>Brandenburg</td>
    <td>Germany</td>
  </tr>
  <tr class="item">
    <td>Laughing Bacchus Wine Cellars</td>
    <td>Vancouver</td>
    <td>Canada</td>
  </tr>
  <tr class="item">
    <td>Magazzini Alimentari Riuniti</td>
    <td>Bergamo</td>
    <td>Italy</td>
  </tr>
  <tr class="item">
    <td>North/South</td>
    <td>London</td>
    <td>UK</td>
  </tr>
  <tr class="item">
    <td>Paris spécialités</td>
    <td>Paris</td>
    <td>France</td>
  </tr>
  <tr class="item">
    <td>Rattlesnake Canyon Grocery</td>
    <td>Albuquerque</td>
    <td>USA</td>
  </tr>
  <tr class="item">
    <td>Simons bistro</td>
    <td>København</td>
    <td>Denmark</td>
  </tr>
  <tr class="item">
    <td>The Big Cheese</td>
    <td>Portland</td>
    <td>USA</td>
  </tr>
  <tr class="item">
    <td>Vaffeljernet</td>
    <td>Århus</td>
    <td>Denmark</td>
  </tr>
  <tr class="item">
    <td>Wolski Zajazd</td>
    <td>Warszawa</td>
    <td>Poland</td>
  </tr>
  </tbody>
</table>
</body>

ALL the (SNIPPET)code :

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://www.w3schools.com/w3css/4/w3.css" rel="stylesheet" />
<style>
  
* {
  box-sizing: border-box;
}

#myInput {
  background-image: url('/css/searchicon.png');
  background-position: 10px 10px;
  background-repeat: no-repeat;
  width: 100%;
  font-size: 16px;
  padding: 12px 20px 12px 40px;
  border: 1px solid #ddd;
  margin-bottom: 12px;
}

#myTable {
  border-collapse: collapse;
  width: 100%;
  border: 1px solid #ddd;
  font-size: 18px;
}

#myTable th, #myTable td {
  text-align: left;
  padding: 12px;
}

#myTable tr {
  border-bottom: 1px solid #ddd;
}

#myTable tr.header, #myTable tr:hover {
  background-color: #f1f1f1;
}
</style>
</head>
<body>
<p class="w3-center"><button id="buttonReset" onclick="performReset()" class="w3-button w3-grey w3-hover-light-grey">Reset</button></p>

<h2>My Customers</h2>

<input type="text" id="myInput" onkeyup="filterTable()" placeholder="Search for names.." title="Type in a name">

<table id="myTable">
  <tr class="header">
    <th style="width:60%;">Name</th>
    <th style="width:20%;">City</th>
    <th style="width:20%;">Country</th>
  </tr>
  <tbody>
  <tr class="item">
    <td>Alfreds Futterkiste</td>
    <td>Berlin</td>
    <td>Germany</td>
  </tr>
  <tr class="item">
    <td>Berglunds snabbköp</td>
    <td>Lule </td>
    <td>Sweden</td>
  </tr>
  <tr class="item">
    <td>Centro comercial Moctezuma</td>
    <td>México D.F.</td>
    <td>Mexico</td>
  </tr>
  <tr class="item">
    <td>Ernst Handel</td>
    <td>Graz</td>
    <td>Austria</td>
  </tr>
  <tr class="item">
    <td>FISSA Fabrica Inter. Salchichas S.A.</td>
    <td>Madrid</td>
    <td>Spain</td>
  </tr>
  <tr class="item">
    <td>Galería del gastrónomo</td>
    <td>Barcelona</td>
    <td>Spain</td>
  </tr>
  <tr class="item">
    <td>Island Trading</td>
    <td>Cowes</td>
    <td>UK</td>
  </tr>
  <tr class="item">
    <td>Königlich Essen</td>
    <td>Brandenburg</td>
    <td>Germany</td>
  </tr>
  <tr class="item">
    <td>Laughing Bacchus Wine Cellars</td>
    <td>Vancouver</td>
    <td>Canada</td>
  </tr>
  <tr class="item">
    <td>Magazzini Alimentari Riuniti</td>
    <td>Bergamo</td>
    <td>Italy</td>
  </tr>
  <tr class="item">
    <td>North/South</td>
    <td>London</td>
    <td>UK</td>
  </tr>
  <tr class="item">
    <td>Paris spécialités</td>
    <td>Paris</td>
    <td>France</td>
  </tr>
  <tr class="item">
    <td>Rattlesnake Canyon Grocery</td>
    <td>Albuquerque</td>
    <td>USA</td>
  </tr>
  <tr class="item">
    <td>Simons bistro</td>
    <td>København</td>
    <td>Denmark</td>
  </tr>
  <tr class="item">
    <td>The Big Cheese</td>
    <td>Portland</td>
    <td>USA</td>
  </tr>
  <tr class="item">
    <td>Vaffeljernet</td>
    <td>Århus</td>
    <td>Denmark</td>
  </tr>
  <tr class="item">
    <td>Wolski Zajazd</td>
    <td>Warszawa</td>
    <td>Poland</td>
  </tr>
  </tbody>
</table>

<script>
  function performReset() {
  document.getElementById("myInput").value = "";
  filterTable();
}

function filterTable() {
  var input, filter, table, tr, i,name,country,city;
  input = document.getElementById("myInput");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");
  for (i = 0; i < tr.length; i++) {
    //write here which column you want filter
    name=tr[i].getElementsByTagName("td")[0];
    city=tr[i].getElementsByTagName("td")[1];
    country=tr[i].getElementsByTagName("td")[2];

    if (name) {
      if (name.innerHTML.toUpperCase().indexOf(filter) > -1 ||city.innerHTML.toUpperCase().indexOf(filter) > -1 || country.innerHTML.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }
  }
}
</script>

</body>
</html>

2 Comments

Your code runs nicely, but I want each column to have its own independent input / filter. However, your code helps me with my next project where I want to have a single "smart search" input for a table. Thank you.
@Ali Kakakhel nice... Have fun for your next project.. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.