Restrict second table filter to results of first filter? |
Restrict second table filter to results of first filter? |
EdNerd |
May 14 2020, 01:18 PM
Post
#1
|
Group: Members Posts: 8 Joined: 12-May 20 Member No.: 27,340 |
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 |
May 14 2020, 05:38 PM
Post
#2
|
. Group: WDG Moderators Posts: 9,656 Joined: 10-August 06 Member No.: 7 |
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. This post has been edited by Christian J: May 15 2020, 12:36 PM |
EdNerd |
May 15 2020, 12:08 PM
Post
#3
|
Group: Members Posts: 8 Joined: 12-May 20 Member No.: 27,340 |
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 |
May 15 2020, 12:35 PM
Post
#4
|
. Group: WDG Moderators Posts: 9,656 Joined: 10-August 06 Member No.: 7 |
You're welcome!
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. 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 |
May 16 2020, 05:40 PM
Post
#5
|
Group: Members Posts: 8 Joined: 12-May 20 Member No.: 27,340 |
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 |
May 17 2020, 08:08 AM
Post
#6
|
. Group: WDG Moderators Posts: 9,656 Joined: 10-August 06 Member No.: 7 |
I was more wondering how it handled a Null or blank string from one of the inputs. Good question! 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" 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 |
May 18 2020, 01:52 PM
Post
#7
|
Group: Members Posts: 8 Joined: 12-May 20 Member No.: 27,340 |
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 |
Lo-Fi Version | Time is now: 24th April 2024 - 06:53 PM |