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.
CodePudding user response:
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>
CodePudding user response:
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
}
}
}
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
}
})
}
CodePudding user response:
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.