brandoncalvin
brandoncalvin

Reputation: 57

Filter table columns in Vanilla JavaScript with multiple inputs

Each column filter, filters its own column, but when trying to filter another column, previous column filters are not remembered.

For example, in the given table: If I search "Basketball" as the sport, then want to narrow my results and search for a specific team in the "Team" column, my results from the "Sport" column are no longer included because the table has reset.

I want to make this usable regardless of the input fields and corresponding columns. So I could use it on multiple tables if need be. That is why I used:

// if data-label matches input name - run
   if(cells[j].getAttribute('data-label').match(inputName)){
       // do stuff
   }     

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Filter Table by Column</title>
    <link rel="stylesheet" href="css/style.css
    ">
</head>
<body>
<table>
    <thead>
        <tr>
            <td><input type="text" name="Player" placeholder="Player... "></td>
            <td><input type="text" name="Sport" placeholder="Sport..."></td>
            <td><input type="text" name="Team" placeholder="Team..."></td>
        </tr>
    </thead>

    <tbody>
        <tr>
            <td data-label="Player">Michael Jordan</td>
            <td data-label="Sport">Basketball</td>
            <td data-label="Team">Chicago Bulls</td>
        </tr>

        <tr>
            <td data-label="Player">Kobe Bryant</td>
            <td data-label="Sport">Basketball</td>
            <td data-label="Team">LA Lakers</td>
        </tr>
        <tr>
            <td data-label="Player">Brett Favre</td>
            <td data-label="Sport">Football</td>
            <td data-label="Team">Greenbay Packers</td>
        </tr>
        <tr>
            <td data-label="Player">Babe Ruth</td>
            <td data-label="Sport">Baseball</td>
            <td data-label="Team">New York Yankees</td>
        </tr>
        <tr>
            <td data-label="Player">Tom Brady</td>
            <td data-label="Sport">Football</td>
            <td data-label="Team">New England Patriots</td>
        </tr>
        <tr>
            <td data-label="Player">LeBron James</td>
            <td data-label="Sport">Basketball</td>
            <td data-label="Team">LA Lakers</td>
        </tr>
        <tr>
            <td data-label="Player">Steph Curry</td>
            <td data-label="Sport">Basketball</td>
            <td data-label="Team">Golden State Warriors</td>
        </tr>
        <tr>
            <td data-label="Player">Jose Berrios</td>
            <td data-label="Sport">Baseball</td>
            <td data-label="Team">Minnesota Twins</td>
        </tr>
        <tr>
            <td data-label="Player">Kirby Pucket</td>
            <td data-label="Sport">Baseball</td>
            <td data-label="Team">Minnesota Twins</td>
        </tr>
        <tr>
            <td data-label="Player">Zach Parise</td>
            <td data-label="Sport">Hockey</td>
            <td data-label="Team">Minnesota Wild</td>
        </tr>

        <tr>
            <td data-label="Player">Jason Zucker</td>
            <td data-label="Sport">Hockey</td>
            <td data-label="Team">Minnesota Wild</td>
        </tr>
    </tbody>
</table>


<script src="js/main.js"></script>
</body>
</html>

main.js

// Listen to all clicks on the document
document.addEventListener('keyup', function(e){

// if event doesn't match
if(!e.target.matches('input[type="text"]')) return;

    // otherwise run
    filterTable(e);

}, false);

// filter results
function filterTable(e){

let inputName = e.target.name;
let filter = e.target.value.toUpperCase();
let rows = document.querySelector('table tbody').rows;

// get each row
for(let i = 0; i < rows.length; i++){

    // loop through each cell
    let cells = rows[i].cells;
    for(j = 0; j < cells.length; j++){

        let rowContent = cells[j].textContent;

        // if data-label matches input name - run
        if(cells[j].getAttribute('data-label').match(inputName)){

            // if rowContent inlcudes input - run
            if(rowContent.toUpperCase().includes(filter)){
                // show row
                rows[i].style.display = "";
            } else {
                // hide row
                rows[i].style.display = "none";
            }
        }
    }
}
}

style.css

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    height: 100%;
    line-height: 1.5;
}

input {
    padding: 8px;
    border: 0;
    border-bottom: 1px solid #ccc;
    width: 100%;
}

input:focus {
    outline: none;
}

table {
    border-collapse: collapse;
    width: 100%;
    margin-top: 20px;
    padding: 8px;
}

table td {
    padding: 8px;
}

table tbody tr:hover {
    background: #999;
}

Upvotes: 2

Views: 3380

Answers (3)

Slai
Slai

Reputation: 22876

Can be simplified with array of filters :

function filterTable() {
  const query = q => document.querySelectorAll(q);
  const filters = [...query('th input')].map(e => new RegExp(e.value, 'i'));

  query('tbody tr').forEach(row => row.style.display = 
    filters.every((f, i) => f.test(row.cells[i].textContent)) ? '' : 'none');
}
<table>
  <thead>
    <tr>
      <th><input onkeyup="filterTable()" placeholder="Player... "></th>
      <th><input onkeyup="filterTable()" placeholder="Sport..."></th>
      <th><input onkeyup="filterTable()" placeholder="Team..."></th>
    </tr>
  </thead>
  <tbody>
    <tr><td> Michael Jordan </td><td> Basketball </td><td> Chicago Bulls         </td></tr>
    <tr><td> Kobe Bryant    </td><td> Basketball </td><td> LA Lakers             </td></tr>
    <tr><td> Brett Favre    </td><td> Football   </td><td> Greenbay Packers      </td></tr>
    <tr><td> Babe Ruth      </td><td> Baseball   </td><td> New York Yankees      </td></tr>
    <tr><td> Tom Brady      </td><td> Football   </td><td> New England Patriots  </td></tr>
    <tr><td> LeBron James   </td><td> Basketball </td><td> LA Lakers             </td></tr>
    <tr><td> Steph Curry    </td><td> Basketball </td><td> Golden State Warriors </td></tr>
    <tr><td> Jose Berrios   </td><td> Baseball   </td><td> Minnesota Twins       </td></tr>
    <tr><td> Kirby Pucket   </td><td> Baseball   </td><td> Minnesota Twins       </td></tr>
    <tr><td> Zach Parise    </td><td> Hockey     </td><td> Minnesota Wild        </td></tr>
    <tr><td> Jason Zucker   </td><td> Hockey     </td><td> Minnesota Wild        </td></tr>
  </tbody>
</table>

Upvotes: 4

coffidev
coffidev

Reputation: 999

I use a object to store the different queries and then check each one when the input of any one change.

You can find a working demo here: https://codesandbox.io/s/suspicious-flower-80c8d

my js looks like this:

const queries = {};

const inputs = [...document.querySelectorAll("input")];

for (let input of inputs) {
  input.addEventListener("input", e => {
    const name = input.name;
    queries[name] = input.value;
    filterTable();
  });
}

function filterRow(row, query, label) {
  if (query) {
    const text = row.querySelector('td[data-label="' + label + '"]').innerText;
    if (text.toLowerCase().indexOf(query.toLowerCase()) < 0) {
      row.style.display = "none";
    }
  }
}

function filterTable() {
  document.querySelectorAll("tr").forEach((row, index) => {
    if (index === 0) {
      return;
    }

    row.style.display = "table-row";

    for (let key in queries) {
      filterRow(row, queries[key], key);
    }
  });
}

Upvotes: 0

weegee
weegee

Reputation: 3409

You can add an event listener on when the user starts to type and then match the input with the element's value. For all the td elements, check for those which matches to the string input using .includes and then change the visibility of that element to hidden if it doesn't match if it does then make it visible.

Now to filter out Teams and Sport, just do the same for the next element and the previous because they are consecutive.

document.getElementsByName("Player")[0].onkeyup = function() {
  let value = this.value
  Array.from(document.querySelectorAll('td[data-label="Player"]')).forEach((ele) => {
    if (value != '') {
      ele.style.visibility = "visible" // first display all the elements then decide what to display and what not to.
      if (ele.innerHTML.toLowerCase().includes(value.toLowerCase())) { // match the lower cased value so "B == b"
        ele.style.visibility = "visible"
      } else {
        ele.style.visibility = "hidden"
        ele.nextElementSibling.style.visibility = "hidden" // the teams
        ele.nextElementSibling.nextElementSibling.style.visibility = "hidden" // the Sport
      }
    } else {
      ele.style.visibility = "visible"
      ele.nextElementSibling.style.visibility = "visible"
      ele.nextElementSibling.nextElementSibling.style.visibility = "visible"
    }
  })
}
document.getElementsByName("Sport")[0].onkeyup = function() {
  let value = this.value
  Array.from(document.querySelectorAll('td[data-label="Sport"]')).forEach((ele) => {
    if (value != '') {
      ele.style.visibility = "visible"
      if (ele.innerHTML.toLowerCase().includes(value.toLowerCase())) {
        ele.style.visibility = "visible"
        ele.previousElementSibling.style.visibility = "visible"
      } else {
        ele.style.visibility = "hidden"
        ele.previousElementSibling.style.visibility = "hidden"
      }
    } else {
      ele.style.visibility = "visible"
      ele.previousElementSibling.style.visibility = "visible"
    }
  })

}
document.getElementsByName("Team")[0].onkeyup = function() {
  let value = this.value
  Array.from(document.querySelectorAll('td[data-label="Team"]')).forEach((ele) => {
    if (value != '') {
      ele.style.visibility = "visible"
      if (ele.innerHTML.includes(value)) {
        ele.style.visibility = "visible"
        ele.previousElementSibling.style.visibility = "visible"
         ele.previousElementSibling.previousElementSibling.style.visibility = "visible"
      } else {
        ele.style.visibility = "hidden"
        ele.previousElementSibling.style.visibility = "hidden"
                 ele.previousElementSibling.previousElementSibling.style.visibility = "hidden"
      }
    } else {
      ele.style.visibility = "visible"
      ele.previousElementSibling.style.visibility = "visible"
      ele.previousElementSibling.previousElementSibling.style.visibility = "visible"
    }
  })
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    height: 100%;
    line-height: 1.5;
}

input {
    padding: 8px;
    border: 0;
    border-bottom: 1px solid #ccc;
    width: 100%;
}

input:focus {
    outline: none;
}

table {
    border-collapse: collapse;
    width: 100%;
    margin-top: 20px;
    padding: 8px;
}

table td {
    padding: 8px;
}

table tbody tr:hover {
    background: #999;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Filter Table by Column</title>
</head>

<body>
  <table>
    <thead>
      <tr>
        <td><input type="text" name="Player" placeholder="Player... "></td>
        <td><input type="text" name="Sport" placeholder="Sport..."></td>
        <td><input type="text" name="Team" placeholder="Team..."></td>
      </tr>
    </thead>

    <tbody>
      <tr>
        <td data-label="Player">Michael Jordan</td>
        <td data-label="Sport">Basketball</td>
        <td data-label="Team">Chicago Bulls</td>
      </tr>

      <tr>
        <td data-label="Player">Kobe Bryant</td>
        <td data-label="Sport">Basketball</td>
        <td data-label="Team">LA Lakers</td>
      </tr>
      <tr>
        <td data-label="Player">Brett Favre</td>
        <td data-label="Sport">Football</td>
        <td data-label="Team">Greenbay Packers</td>
      </tr>
      <tr>
        <td data-label="Player">Babe Ruth</td>
        <td data-label="Sport">Baseball</td>
        <td data-label="Team">New York Yankees</td>
      </tr>
      <tr>
        <td data-label="Player">Tom Brady</td>
        <td data-label="Sport">Football</td>
        <td data-label="Team">New England Patriots</td>
      </tr>
      <tr>
        <td data-label="Player">LeBron James</td>
        <td data-label="Sport">Basketball</td>
        <td data-label="Team">LA Lakers</td>
      </tr>
      <tr>
        <td data-label="Player">Steph Curry</td>
        <td data-label="Sport">Basketball</td>
        <td data-label="Team">Golden State Warriors</td>
      </tr>
      <tr>
        <td data-label="Player">Jose Berrios</td>
        <td data-label="Sport">Baseball</td>
        <td data-label="Team">Minnesota Twins</td>
      </tr>
      <tr>
        <td data-label="Player">Kirby Pucket</td>
        <td data-label="Sport">Baseball</td>
        <td data-label="Team">Minnesota Twins</td>
      </tr>
      <tr>
        <td data-label="Player">Zach Parise</td>
        <td data-label="Sport">Hockey</td>
        <td data-label="Team">Minnesota Wild</td>
      </tr>

      <tr>
        <td data-label="Player">Jason Zucker</td>
        <td data-label="Sport">Hockey</td>
        <td data-label="Team">Minnesota Wild</td>
      </tr>
    </tbody>
  </table>

</body>

</html>

Upvotes: 0

Related Questions