Javascript Table Search Multiple Criteria

Discussion in 'Programming & Software Development' started by birdie, Jun 6, 2018.

  1. birdie

    birdie Member

    Joined:
    Jun 18, 2002
    Messages:
    2,817
    Location:
    Bundaberg, Queensland
    I'm working on a report for work which has a large HTML table, I want to be able to search/filter it by multiple criteria entered into an input box/boxes.

    I started with this code: https://www.w3schools.com/howto/howto_js_filter_table.asp

    I now have two different styles and neither do exactly what I want.

    HTML:
    <script language="javascript" type="text/javascript"> 
    function searchTable() {
        var input, filter, found, table, tr, td, i, j;
        input = document.getElementById("myInput");
        filter = input.value.toUpperCase();
        table = document.getElementById("servertable");
        tr = table.getElementsByTagName("tr");
        for (i = 1; i < tr.length; i++) {
            td = tr[i].getElementsByTagName("td");
            for (j = 0; j < td.length; j++) {
                if (td[j].innerHTML.toUpperCase().indexOf(filter) > -1) {
                    found = true;
                }
            }
            if (found) {
                tr[i].style.display = "";
                found = false;
            } else {
                tr[i].style.display = "none";
            }
        }
    }
    </script>
    
    <input type='text' placeholder='Search...' id='myInput' onkeyup='searchTable()'>
    
    This one uses one input box and will search every column, but it will only match exactly what is in the input box. So I can't enter the value I want from one column and then space and another value.

    Another method I tried was:
    HTML:
    <script language="javascript" type="text/javascript"> 
    
    function searchTable(col) {
        var input, filter, found, table, tr, td, i;
        input = document.getElementById("myInput" + col);
        filter = input.value.toUpperCase();
        table = document.getElementById("servertable");
        tr = table.getElementsByTagName("tr");
    
        for (i = 0; i < tr.length; i++) {
            td = tr[i].getElementsByTagName("td")[col];
    
            if (td) {
                if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
                    tr[i].style.display = "";
                } else {
                    tr[i].style.display = "none";
                }
            } 
        }
    }
    </script>
    
    <input type='text' placeholder='Search...' id='myInput0' onkeyup="searchTable('0')">
    <input type='text' placeholder='Search...' id='myInput1' onkeyup="searchTable('1')">
    ...etc
    
    This method gives me a input box for each column, I can then search by a particular column. The problem is I want to search by multiple columns, I want what is displayed to meet the criteria in all the input boxes.
    Currently its displaying OR, I want AND.

    Any ideas?
     
  2. akashra

    akashra Member

    Joined:
    Apr 25, 2003
    Messages:
    3,721
    Location:
    Melbourne, AU
    Not gonna check this, but an xpath expression like this should give you a tr element where it contains both tds containing the text value if I remember correctly.
    //table[@id='foo']/tr[td//text()[contains(., 'val1')] and td//text()[contains(., 'val2')]]
     
  3. w0ng

    w0ng Member

    Joined:
    Dec 17, 2006
    Messages:
    125
    Location:
    Sydney
    You're on the right track!

    HTML:
            ...
            // The problem is that here you're saying 
            // "if this column matches the current input, show the row. Otherwise, hide it."
            // whilst ignoring the filtered results from any other input
            if (td) {
                if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
                    tr[i].style.display = "";
                } else {
                    tr[i].style.display = "none";
                }
                 ...
    </script>
    
    There's multiple ways of going about this, but the easiest way is to add some HTML class (e.g. "no-match") to a column when it doesn't match the input and remove that class when it does match.
    Then check if any td in the current tr has a "no-match" class, hide the row. Otherwise, show it.

    HTML:
    <script>
      function searchTable(col) {
        var input, filter, found, table, tr, td, i;
        input = document.getElementById("myInput" + col);
        filter = input.value.toUpperCase();
        table = document.getElementById("servertable");
        tr = table.getElementsByTagName("tr");
    
        for (i = 0; i < tr.length; i++) {
          td = tr[i].getElementsByTagName("td")[col];
    
          if (td) {
            if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
              // Remove class="no-match" if this column matches the input text
              td.classList.remove("no-match");
            } else {
             // Add class="no-match" if this column doesn't match the input text
              td.classList.add("no-match");
            }
          }
    
          var firstNonMatchingTdElementInRow = tr[i].querySelector("td.no-match");
          if (firstNonMatchingTdElementInRow !== null) {
            // Hide row if at least one column doesn't match its input
            tr[i].style.display = "none";
          } else {
            // Show row all columns in the row match its input
            tr[i].style.display = "";
          }
        }
      }
    </script>
    
    Demo: https://codepen.io/w0ng/pen/PaGxbL
     
    wwwww likes this.
  4. OP
    OP
    birdie

    birdie Member

    Joined:
    Jun 18, 2002
    Messages:
    2,817
    Location:
    Bundaberg, Queensland
    w0ng, thanks heaps, that works perfectly :) I knew i was in the ballpark, but I just couldn't think of the right way to do it.
     
  5. mr camouflage

    mr camouflage Member

    Joined:
    May 25, 2012
    Messages:
    635
    Location:
    WA
    This is the sort of thing I'd use datatables for.
     

Share This Page