Search and exclude rows in HTML table

I have a HTML table with over a thousand rows and it’s only getting bigger.
I use this function to search the data in the table.

function myFunction() {
  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("myInput");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td")[0];
    if (td) {
      txtValue = td.textContent || td.innerText;
      if (txtValue.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }
  }
}

and an input box of

<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names..">

This works fine, I can find whatever I type in the box.
But I need another input box, or at least another way, to be able to exclude (hide) rows from the results of the search and also hide rows from the whole table.

So in the first input field I search for COKE and I get:

COKEC001-PAVM-NYC
COKEC001-PAVM-LON
COKEC001-ABCD-NYC
COKEC001-ABCD-LON
COKEC001-ABCD-LON
COKEC002-ABCD-NYC
COKEC002-ABCD-LON
COKEC002-ABCD-LON

now I want to narrow that down to only LON, so I want to be able to type NYC into the Exclude input field. So basically the search is for COKE but not NYC.

Another example is searching for version numbers, search for 8.1 but not 8.1.17 .. that would return rows with 8.1, 8.1.2, 8.1.15, 8.1.18 … many possibilities, but not 8.1.17 … all those rows would be hidden.

So my feeling is there should be a search input field and an exclude input field that can work combined together.

I’m not well versed in javascript. Any help and ideas would be much appreciated.

14 thoughts on “Search and exclude rows in HTML table”

  1. Suggestions while I wait for you to post HTML:

    • Cache the cells, no need to get the rows on every keyup and then the first cells. Put
    const tds = document.querySelectorAll("#myTable tr td:first-child"); 
    

    outside the function but it has to execute after the table is loaded

    • add a style .hide { display: none;} and use
        const val = txtValue.toUpperCase(); 
        tr[i].classList.toggle("hide",val.indexOf(filter) === -1 || 
        arrayOfIgnore.includes(val))
    

    where arrayOfIgnore gets its values from your second input field

    Something like this which I cannot test until you show more code

    const tds = document.querySelectorAll("#myTable tr td:first-child"); 
    document.getElementById("myInput").addEventListener("input",function() {
      const filter = this.value.toUpperCase();
      tds.forEach(td => {
        const txtValue = td.textContent; // always uppercase
        td.closest("tr").classList.toggle("hide", filter !== "" &&
         (arrayOfIgnore.includes(textValue) ||
         !txtValue.includes(filter)));
      })
    });
    
    Reply
  2. You can make another search field text for the excludes names

    <input type="text" id="myInputExclude" onkeyup="myFunction()" placeholder="Search for excludes names.."/>
    
    function myFunction() {
      var input, filter, table, tr, td, i, txtValue;
      input = document.getElementById("myInput");
      inputExclude = document.getElementById("myInputExclude");
      filter = input.value.toUpperCase();
      filterExclude = inputExclude.value.toUpperCase();
      table = document.getElementById("myTable");
      tr = table.getElementsByTagName("tr");
      for (i = 0; i < tr.length; i++) {
        td = tr[i].getElementsByTagName("td")[0];
        if (td) {
          txtValue = td.textContent || td.innerText;
          if (txtValue.toUpperCase().indexOf(filter) > -1 && (filterExclude == "" || txtValue.toUpperCase().indexOf(filterExclude) == -1)) {
            tr[i].style.display = "";
          } else {
            tr[i].style.display = "none";
          }
        }
      }
    }
    

    You should also think about doing a "debounce" in your code. when you type something fast in the filter the query is delayed due to the number of elements

    const debounce = (funct, time) => {
        let timeoutId;
        return function() {
            if(timeoutId) {
                clearTimeout(timeoutId)
            }
            const context = this
            const args = arguments
            timeoutId = setTimeout(() => {
                funct.apply(context, args)
            }, time);
        }
    }
    

    Now on keyup

    <input type="text" id="myInput" onkeyup="debounce(myFunction(), 500)" placeholder="Search for names.."/>
    <input type="text" id="myInputExclude" onkeyup="debounce(myFunction, 500)" placeholder="Search for excludes names.."/>
    

    This will give you a little time between typing so you don’t see a delay

    Reply

Leave a Comment