I have multiple tables on a page. Each table, has a "check all" checkbox
in the header. In the body, there is another checkbox
for each row.
When the user checks each boy row, then a active
class is applied and highlights the marked row, and the counter increases/decreases.
I have a problem with the check all function.
When the user selects the check all checkbox
in the header, then it should select all the rows in just that one table. I can only get it to check all the rows across all the tables. Also the counter counts all the rows across all the tables, rather than just that one table.
Where am I going wrong?
Here is my code:
// https://gomakethings.com/a-vanilla-js-foreach-helper-method/
var forEach = function forEach(arr, callback) {
Array.prototype.forEach.call(arr, callback);
};
var tableInputs = document.querySelectorAll('.table tbody td .form-check-input');
var tableSelectAll = document.querySelectorAll('.table thead th .form-check-input');
var count = document.querySelector('.output span')
forEach(tableInputs, function(element) {
element.addEventListener('change', function() {
// active class to make row blue
if (element.checked) {
element.parentNode.parentNode.classList.add('active');
} else {
element.parentNode.parentNode.classList.remove('active');
}
// set count to -
var numberSelected = 0;
// count number of checked
for (var i = 0; i < tableInputs.length; i++) {
if (tableInputs[i].checked == true) {
numberSelected++;
}
}
// display the count
count.innerHTML = numberSelected;
});
});
forEach(tableSelectAll, function(element) {
element.addEventListener('change', function() {
if (element.checked == true) {
forEach(tableInputs, function(input) {
input.parentNode.parentNode.classList.add('active');
input.checked = true;
// set count to -
var numberSelected = 0;
// count number of checked
for (var i = 0; i < tableInputs.length; i++) {
if (tableInputs[i].checked == true) {
numberSelected++;
}
}
// display the count
count.innerHTML = numberSelected;
});
} else {
forEach(tableInputs, function(input) {
input.parentNode.parentNode.classList.remove('active');
input.checked = false;
count.innerHTML = 0;
});
}
});
});
.form-check-input {
border: solid 1px #000;
position: relative;
}
tr.active {
background-color: lightblue;
}
body { margin: 0; zoom: .88; }
p { margin: 0; }
<div class="container">
<div class="row">
<div class="col-12">
<p>Table 1</p>
<table class="table table-sm table-borderless">
<thead>
<tr>
<th><input class="form-check-input" type="checkbox" value=""></th>
<th>Request date</th>
<th>Name</th>
<th>Organisation/Employer</th>
<th>Selected Course(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Clark Kent</a></td>
<td><span>Daily Planet</span></td>
<td><span>Flight</span></td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Hal Jordan</a></td>
<td><span>Green Lantern Corps</span></td>
<td>Lighting</td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Arthur Curry</a></td>
<td><span>Atlantis Water</span></td>
<td>Aquatics</td>
</tr>
</tbody>
</table>
<p>Table 2</p>
<table class="table table-sm table-borderless ">
<thead>
<tr>
<th><input class="form-check-input" type="checkbox" value=""></th>
<th>Request date</th>
<th>Name</th>
<th>Organisation/Employer</th>
<th>Selected Course(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Barry Allen</a></td>
<td><span>Star Labs</span></td>
<td><span>Speed</span></td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Bruce Wayne</a></td>
<td><span>Wayne Enterprises</span></td>
<td>Combat</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="output">Total selected: <span>0</span></p>
Regardless of the approach one always should break down the problem into specific tasks which for the OP's requirements are ...
initialize a checkbox related change handling.
on any checkbox' state change do update all checkbox depended states.
do update the checkbox counter at init time and at checkbox state change time.
for any (initially) checked checkbox update its related table row state as well.
The techniques/tools are Event Delegation and the Selectors API
At any checkbox state change the handler inspects the event target
whether it belongs to the current table's header or body.
Based on this check one either, for the first case, needs to check/uncheck all of a current table's body-related checkboxes or, according to the second case, one needs to update the state of the sole header-related checkbox.
Updating the checkbox counter is achieved by the correct selector and the queried node list's length value.
function updateCheckboxCounter() {
document
.querySelector('.output > span')
.textContent = document
.querySelectorAll('table.table tbody [type="checkbox"]:checked')
.length;
}
function updateTableRowActiveState(checkboxNode) {
checkboxNode
.closest('tr')
.classList
.toggle('active', checkboxNode.checked);
}
function updateCheckboxDependedStates({ target }) {
const tableNode = target.closest('table.table');
if (target.matches('thead [type="checkbox"]')) {
tableNode
.querySelectorAll('tbody [type="checkbox"]')
.forEach(elmNode => {
elmNode.checked = target.checked;
updateTableRowActiveState(elmNode);
});
} else if (target.matches('tbody [type="checkbox"]')) {
tableNode
.querySelector('thead [type="checkbox"]')
.checked = Array
.from(
target
.closest('tbody')
.querySelectorAll('[type="checkbox"]')
)
.every(elmNode => elmNode.checked);
updateTableRowActiveState(target);
}
updateCheckboxCounter();
}
function init() {
document
.querySelectorAll('table.table')
.forEach(elmNode =>
elmNode.addEventListener('change', updateCheckboxDependedStates)
);
document
.querySelectorAll('table.table tbody [type="checkbox"]:checked')
.forEach(updateTableRowActiveState);
updateCheckboxCounter();
}
init();
.form-check-input {
border: solid 1px #000;
position: relative;
}
tr.active {
background-color: lightblue;
}
body { margin: 0; zoom: .88; }
p { margin: 0; }
<div class="container">
<div class="row">
<div class="col-12">
<p>Table 1</p>
<table class="table table-sm table-borderless">
<thead>
<tr>
<th><input class="form-check-input" type="checkbox" value=""></th>
<th>Request date</th>
<th>Name</th>
<th>Organisation/Employer</th>
<th>Selected Course(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Clark Kent</a></td>
<td><span>Daily Planet</span></td>
<td><span>Flight</span></td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Hal Jordan</a></td>
<td><span>Green Lantern Corps</span></td>
<td>Lighting</td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value="" checked></td>
<td>10/10/2014</td>
<td><a href="#">Arthur Curry</a></td>
<td><span>Atlantis Water</span></td>
<td>Aquatics</td>
</tr>
</tbody>
</table>
<p>Table 2</p>
<table class="table table-sm table-borderless ">
<thead>
<tr>
<th><input class="form-check-input" type="checkbox" value=""></th>
<th>Request date</th>
<th>Name</th>
<th>Organisation/Employer</th>
<th>Selected Course(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td><input class="form-check-input" type="checkbox" value="" checked></td>
<td>10/10/2014</td>
<td><a href="#">Barry Allen</a></td>
<td><span>Star Labs</span></td>
<td><span>Speed</span></td>
</tr>
<tr>
<td><input class="form-check-input" type="checkbox" value=""></td>
<td>10/10/2014</td>
<td><a href="#">Bruce Wayne</a></td>
<td><span>Wayne Enterprises</span></td>
<td>Combat</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p class="output">Total selected: <span>0</span></p>