Help - Search - Members - Calendar
Full Version: Restrict second table filter to results of first filter?
HTMLHelp Forums > Programming > Client-side Scripting
EdNerd
I copied code from W3Schools to filter a table for the text in an input and hide all rows that don't match. The function works only on the first column - <td> element [0]. If I change that to element [1], it filters on the second column - only.

I tried to add a second input, copy the function, and rename items as required in the hopes of filtering first on the first column, and then filter any duplicate results on the second column. Unfortunately, that didn't work as planned. The hidden table rows are still there, and the second filter will unhide any previously de-selected rows if the value happens to match the second filter. If I keep going long enough, I do eventually get into my first filter results. But that's too much.

Is there an easy way to restrict the second filter function to looking at only the results of the first filter?
Maybe assign a style or such to the first results and use that as a criteria in the second filter??
Ed

Input and Table CSS:
CODE

<style>

#myInput1 {
  /* background-image: url('/css/searchicon.png'); Add a search icon to input */
  /* background-position: 10px 12px; Position the search icon */
  /* background-repeat: no-repeat; Do not repeat the icon image */
  width: 35%; /* Full-width */
  font-size: 16px; /* Increase font-size */
  padding: 12px 20px 12px 40px; /* Add some padding */
  border: 1px solid #ddd; /* Add a grey border */
  margin-bottom: 12px; /* Add some space below the input */
}

#myInput2 {
  /* background-image: url('/css/searchicon.png'); Add a search icon to input */
  /* background-position: 10px 12px; Position the search icon */
  /* background-repeat: no-repeat; Do not repeat the icon image */
  width: 35%; /* Full-width */
  font-size: 16px; /* Increase font-size */
  padding: 12px 20px 12px 40px; /* Add some padding */
  border: 1px solid #ddd; /* Add a grey border */
  margin-bottom: 12px; /* Add some space below the input */
}

#myTable {
  border-collapse: collapse; /* Collapse borders */
  width: 75%; /* Full-width */
  border: 1px solid #ddd; /* Add a grey border */
  font-size: 18px; /* Increase font-size */
  margin-left: 1em
}

#myTable th, #myTable td {
  text-align: left; /* Left-align text */
  padding: 12px; /* Add padding */
  border-right: 1px solid #ddd;
}

#myTable tr {
  /* Add a bottom border to all table rows */
  border-bottom: 1px solid #ddd;
}

#myTable tr.header, #myTable tr:hover {
  /* Add a grey background color to the table header and on hover */
  background-color: #f1f1f1;
}

</style>


Inputs:
CODE

<input type="text" id="myInput1" onkeyup="myFunctionLN()" placeholder="Search for Last Name ...">
<input type="text" id="myInput2" onkeyup="myFunctionFN()" placeholder="Search for First Name ...">


Functions:
CODE

<script>
function myFunctionLN() {
  // Declare variables
  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("myInput1");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");

  // Loop through all table rows, and hide those who don't match the search query
  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";
      }
    }
  }
}

function myFunctionFN() {
  // Declare variables
  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("myInput2");
  filter = input.value.toUpperCase();
  table = document.getElementById("myTable");
  tr = table.getElementsByTagName("tr");

  // Loop through all table rows, and hide those who don't match the search query
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td")[1];
    if (td) {
      txtValue = td.textContent ||  td.innerText;
      if (txtValue.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    }
  }
}
</script>
Christian J
QUOTE(EdNerd @ May 14 2020, 08:18 PM) *

Is there an easy way to restrict the second filter function to looking at only the results of the first filter?

Here's a quick idea. I let a single function check both Lastname and Firstname at once:

CODE

<script>
function myFunction()
{
    var input0, input1, filter0, filter1, table, tr, td, i, txtValue0, txtValue1;

    input0 = document.getElementById("myInput0");
    input1 = document.getElementById("myInput1");

    filter0 = input0.value.toUpperCase();
    filter1 = input1.value.toUpperCase();

    table = document.getElementById("myTable");
    tr = table.getElementsByTagName("tr");

    for (i = 0; i < tr.length; i++)
    {
        td0 = tr[i].getElementsByTagName("td")[0];
        td1 = tr[i].getElementsByTagName("td")[1];
        if (td0 && td1)
        {
            txtValue0 = td0.textContent ||  td0.innerText;
            txtValue1 = td1.textContent ||  td1.innerText;

            if (txtValue0.toUpperCase().indexOf(filter0) > -1 && txtValue1.toUpperCase().indexOf(filter1) > -1)
            {
                tr[i].style.display = "";
            }
            else
            {
                tr[i].style.display = "none";
            }
        }
    }
}
</script>


I also renamed the INPUT IDs for consistency:

CODE

<input type="text" id="myInput0" onkeyup="myFunction()" placeholder="Search for Last Name ...">
<input type="text" id="myInput1" onkeyup="myFunction()" placeholder="Search for First Name ...">



QUOTE
Maybe assign a style or such to the first results and use that as a criteria in the second filter??

That might work too, at least if the table is not too large.
EdNerd
Beautiful, Christian!! Absolutely fantastic!
Thank you!! Thank you!! Thank you!! Thank you!!

Can you illuminate on the && operator? Looks like that's a major key to this puzzle.

Ed
Christian J
You're welcome! smile.gif

I'm doing some tests with highlighting as well, but it's actually more tricky since there are so many ways to do it. For example, it could highlight:

- Only when both Lastname and Firstname match.

- When either name matches.

- Initially when either names matches, until you get a match with both names; then only highlight that.

QUOTE(EdNerd @ May 15 2020, 07:08 PM) *

Can you illuminate on the && operator? Looks like that's a major key to this puzzle.

It's an AND logical operator, simply put both expressions on either side must be true in order for the whole IF statement to be true.

There seems to be a lot of more intricate details though, that I'm not qualified to elaborate on.
EdNerd
I was more wondering how it handled a Null or blank string from one of the inputs.

Let's see if I can follow this (comments inline in code):

for (i = 0; i < tr.length; i++)
>> Start at tr=0, and loop until we run out of rows
{
td0 = tr.getElementsByTagName("td")[0];
[i]>> Get the first cell (td) as an object

td1 = tr.getElementsByTagName("td")[1];
[i]>> Get the second td as an object

if (td0 && td1)
>> If you got both a first and a second td
{
txtValue0 = td0.textContent || td0.innerText;
>> Get the value of the text in the first td
txtValue1 = td1.textContent || td1.innerText;
>> Get the value of the text in the second td

if (txtValue0.toUpperCase().indexOf(filter0) > -1 && txtValue1.toUpperCase().indexOf(filter1) > -1)
>> Here's where I'm wondering about the zero-length string in an input. This would seem to say: "As long as you
>> have a filtering match in both td (both conditions are TRUE, display the row. If not, display = "none" - hide the row.
>> But when we enter the first letter in Last Name and the filtering begins, First Name is blank - but I don't have a blank
>> td to match?? How does it match from a blank input? I created another tr that had a blank td[1], and the match
>> still worked. Hence my question of how this handles zero-length / blank / Null values??

{
tr[i].style.display = "";
}
else
{
tr[i].style.display = "none";
}
}
}
Christian J
QUOTE(EdNerd @ May 17 2020, 12:40 AM) *

I was more wondering how it handled a Null or blank string from one of the inputs.

Good question! blink.gif

First, it seem an empty INPUT default value returns an empty string, not null:

CODE
<input type="text" id="foo" value="">

<script type="text/javascript">
alert(typeof document.getElementById('foo').value); // "string"
</script>

This might be the relevant part of the spec:

"the user agent must set the value of the element to the value of the value content attribute, if there is one, or the empty string otherwise"
https://html.spec.whatwg.org/multipage/inpu...ttr-input-value


It also seems indexOf returns "0" when searching for an empty string, not "-1" as one might think:

CODE
<script type="text/javascript">
alert('foo'.indexOf('')); // "0"
</script>


I have no idea why this happens, a web search turned up various discussions that seem to say that's how the spec is defined. See e.g. https://stackoverflow.com/questions/5262418...ing-is-zero-why

BTW, javascript comments must look like this:

CODE
// single line comment

/*  Multiple
line comment */

See also https://developer.mozilla.org/en-US/docs/We...rammar#Comments
EdNerd
Thank you for the answer. (Though admittedly most of it went over my head!)

I knew about marking comments - I just didn't want to make the whole thing a code block. It just made it easier to do the comments.

Thank you so much for all your help in this!!
Ed
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2020 Invision Power Services, Inc.