theo
theo

Reputation: 299

Check all checkboxes in a table which row is not hidden

I have a table with checkbox in every row and filtering mechanism.

        function toggle(source) {
            checkboxes = document.getElementsByName('cbox');
            for (var i = 0, n = checkboxes.length; i < n; i++) {
                checkboxes[i].checked = source.checked;
            }
        }
        
        function FilterTable() {
                
            var input, filter, table, tr, td, i, txtValue;
            input = document.getElementById("txtFilter");
            filter = input.value.toUpperCase();
            table = document.getElementById("tblEmployees");
            tr = table.getElementsByTagName("tr");

            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";
                    }
                }
            }
        }
<input type="text" id="txtFilter" onkeyup="FilterTable()"  />
 <table id="tblEmployees">
    <thead>
        <tr>
            <th><input type="checkbox" onClick="toggle(this)"></th>
            <th>Name</th>
        </tr>
    </thead>
    <tbody>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 2</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 3</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 2</td>
        </tr>
    </tbody>
  </table>

Currently, when I check the main checkboxes in the header, all checkbox in the table gets checked, even if the row is hidden when I filter the table. What I need to do is to check only the checkboxes that are not hidden.

For example, I filter "John". 3 rows will remain visible and the 2 "Mark" records will be hidden. Then, I tick the checkbox in the header, only the 3 "John" records should be checked. So when I clear the filter, the 2 "Mark" records remain unchecked.

Upvotes: 0

Views: 89

Answers (3)

Lain
Lain

Reputation: 3726

Version 1: Minimal change on your code

You simply need to check for the style.display of the parenting row in your function toggle.

function toggle(source){
    checkboxes = document.getElementsByName('cbox');
    for(var i = 0, n = checkboxes.length; i < n; i++){
        //REM: Fetch the closest <tr> element
        const tTR = checkboxes[i].closest('tr');
        
        //REM: Only change if not display "none"
        if(tTR.style.display !== 'none'){
            checkboxes[i].checked = source.checked
        }
    }
}

Version 2: Adding forEach

Instead of style.display you could also make use of the hidden property and instead of a for-loop you could go for forEach.

function toggle(source){
    document.getElementsByName('cbox').forEach(checkbox => {
        //REM: Only change if not display "none"
        if(checkbox.closest('tr').style.display !== 'none'){
            checkbox.checked = source.checked
        }
    })
}

Version 3: Use classes instead of styles

This is the way I would approach it, using classes instead of setting style.

function toggle(source){
  document.querySelectorAll('tr:not(.Hidden) [name=cbox]').forEach(checkbox => {
    checkbox.checked = source.checked
  })
};
        
function FilterTable(){             
  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("txtFilter");
  filter = input.value.toUpperCase();
  table = document.getElementById("tblEmployees");
  tr = table.getElementsByTagName("tr");

  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].classList.remove('Hidden')
          } else {
              tr[i].classList.add('Hidden')
          }
      }
  }
};
.Hidden{
  display: none
}
<input type="text" id="txtFilter" onkeyup="FilterTable()"  />
 <table id="tblEmployees">
    <thead>
        <tr>
            <th><input type="checkbox" onClick="toggle(this)"></th>
            <th>Name</th>
        </tr>
    </thead>
    <tbody>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 2</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 3</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 2</td>
        </tr>
    </tbody>
  </table>

Upvotes: 3

ChrisM
ChrisM

Reputation: 1672

There's several ways to do this but a simple way to solve it would be to add a data attribute to your hidden rows and then check for that in your filter function.

I'd recommend doing this over checking for a style property to avoid coupling your script too closely to the particulars of how something is styled.

function toggle(source) {
            var checkboxes = document.getElementsByName('cbox');
            var tr = document.querySelectorAll("tbody tr");
            for (var i = 0, n = checkboxes.length; i < n; i++) {
                if(tr[i].dataset.hidden === "true"){
                  continue;
                 }
                checkboxes[i].checked = source.checked;
            }
        }
        
        function FilterTable() {
                
            var input, filter, table, tr, td, i, txtValue;
            input = document.getElementById("txtFilter");
            filter = input.value.toUpperCase();
            table = document.getElementById("tblEmployees");
            tr = table.getElementsByTagName("tr");

            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].dataset.hidden = "false";
                        tr[i].style.display = "";
                    } else {
                        tr[i].dataset.hidden = "true";
                        tr[i].style.display = "none";
                    }
                }
            }
        }
<input type="text" id="txtFilter" onkeyup="FilterTable()"  />
 <table id="tblEmployees">
    <thead>
        <tr>
            <th><input type="checkbox" onClick="toggle(this)"></th>
            <th>Name</th>
        </tr>
    </thead>
    <tbody>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 2</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>John 3</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 1</td>
        </tr>
        <tr>
        <td><input type="checkbox" name="cbox"/></td>
        <td>Mark 2</td>
        </tr>
    </tbody>
  </table>

You could also do the same more directly with a 'hidden' attribute, and avoid the redundancy of having both a 'style' and a data attribute.

Upvotes: 1

GreyRoofPigeon
GreyRoofPigeon

Reputation: 18123

You want to check if the table-row is hidden before you check the box.

So, within your for loop I've added an if statement which searches for the closest('tr'), and checks if it's hidden or not.

function toggle(source) {
  checkboxes = document.getElementsByName('cbox');
  for (var i = 0, n = checkboxes.length; i < n; i++) {
    if(checkboxes[i].closest('tr').style.display != "none") {
      checkboxes[i].checked = source.checked;
    }
  }
}

function FilterTable() {

  var input, filter, table, tr, td, i, txtValue;
  input = document.getElementById("txtFilter");
  filter = input.value.toUpperCase();
  table = document.getElementById("tblEmployees");
  tr = table.getElementsByTagName("tr");

  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";
      }
    }
  }
}
<input type="text" id="txtFilter" onkeyup="FilterTable()" />
<table id="tblEmployees">
  <thead>
    <tr>
      <th><input type="checkbox" onClick="toggle(this)"></th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><input type="checkbox" name="cbox" /></td>
      <td>John 1</td>
    </tr>
    <tr>
      <td><input type="checkbox" name="cbox" /></td>
      <td>John 2</td>
    </tr>
    <tr>
      <td><input type="checkbox" name="cbox" /></td>
      <td>John 3</td>
    </tr>
    <tr>
      <td><input type="checkbox" name="cbox" /></td>
      <td>Mark 1</td>
    </tr>
    <tr>
      <td><input type="checkbox" name="cbox" /></td>
      <td>Mark 2</td>
    </tr>
  </tbody>
</table>

Upvotes: 2

Related Questions