admin管理员组

文章数量:1291002

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 pletely 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>

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 pletely 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.

Share Improve this question edited Apr 13, 2020 at 14:22 Rohit Girdhar asked Apr 13, 2020 at 14:00 Rohit GirdharRohit Girdhar 3534 gold badges9 silver badges26 bronze badges 7
  • Well, I think, you need a kinda source values list and visible values list. The source one is an input to your main filter function which returns a subset of the list (visible values). You should always apply all filters to your source list. Keyup event listeners should only change params for that filters. – Ivan Burnaev Commented Apr 13, 2020 at 14:06
  • Can you please elaborate on what do you mean by source values list? – Rohit Girdhar Commented Apr 13, 2020 at 14:23
  • FYI: instead of onkeyup you probably want to use oninput. As it is now if I copy and paste text using the mouse it won't filter since no keys were pressed. – user128511 Commented Apr 13, 2020 at 14:29
  • Does this answer your question? Filtering table multiple columns – Heretic Monkey Commented Apr 13, 2020 at 14:36
  • @HereticMonkey Unfortunately it doesn't because, that answer is using one input filter across multiple columns whereas I'm looking for multiple column filter using several input fields. – Rohit Girdhar Commented Apr 13, 2020 at 14:42
 |  Show 2 more ments

4 Answers 4

Reset to default 6

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 ment)

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>

You can bine 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";
            }
        }
    }
}

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:

  • custom filtering behavious
  • pure functions which can be easily tested
  • posable main filter function
  • no imperative mess =) (debatable)

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>

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 file

tr = table.querySelectorAll("tbody tr:not(.header)");

table header missing after search

本文标签: javascriptHTML Table Multiple Column FiltersStack Overflow