I have a datatable for class attendance that is comprised of a student name and 4 columns of checkboxes per row. The Select All column is for the user to dynamically set checked/unchecked attribute for the remaining checkboxes in the row. Ultimately, the form will be saved and the database updated with the value of the checkboxes. When the form is presented, the database does not contain a record of what is presented to the user, it will be inserted when the form is saved.
Select All Student Name On Time Present Contributing Prep Lesson
x Mickey Mouse o o o o
HTML:
<table id="UserTable" >
<thead>
<th style="text-align:center;">Select All</th>
<th style="text-align:center;">Student Name</th>
<th style="text-align:center;">On Time</th>
<th style="text-align:center;">Present</th>
<th style="text-align:center;">Contributing</th>
<th style="text-align:center;">Prep. Lesson</th>
</thead>
<tbody>
<?php if(!empty($students)) { ?>
<?php foreach($students as $student) { ?>
<tr>
<div >
<!-- select all -->
<td style="text-align:center;"><input type="checkbox" id="select-all" onchange="selectAll(this)"></td>
<!-- student -->
<td ><?php echo $student['first_name'] . ' ' . $student['last_name'] ?></td>
<!-- on-time -->
<td style="text-align:center;"><input type="checkbox" id="on-time"></td>
<!-- Present -->
<td style="text-align:center;"><input type="checkbox" id="present"></td>
<!-- contributing -->
<td style="text-align:center;"><input type="checkbox" id="contribute"></td>
<!-- prepared lesson -->
<td style="text-align:center;"><input type="checkbox" id="prep-lesson"></td>
</tr>
<?php }} ?>
</tbody>
</table>
Attempts at Javascript code which do not work:
<script type="text/javascript">
$(document).ready(function(){
$('#UserTable').DataTable();
});
$('#UserTable tbody').on( 'click', 'tr td:eq(0)', function () {
//var onTime = $(this).parents('#userTable tbody").siblings('#on-time');
//$(onTime).attr('checked', true);
alert("col1");
});
function selectAll(elem) {
alert('in onChange');
var table = $('#UserTable').DataTable();
if(elem.checked) {
// var onTime = $(this).parents('#userTable tbody').siblings('.on-time');
// var colData = table.column(this).data();
// $(onTime).attr('checked', true);
alert('checked');
}
else {
alert ('unchecked');
}
}
</script>
Thanks for your help,
Dennis
CodePudding user response:
In a group with a “check all” checkbox, the grouping checkbox should also update dependent on the other checkboxes’ states. So after checking “check all“ and removing one dependent check, it should represent that indeterminate state.
So the suggestion here does the following:
- Establish groups of checkboxes by iterating on
<tr>
elements - Extract the first checkbox found inside as the grouping checkbox
- Update dependent checkboxes if the grouping one changes state by binding to the `change' event
- And update the grouping checkbox if one of the dependent boxes change state
- Filter the list of checkboxes to find those that are checked with Array.filter()
- Count the checked checkboxes
- Uncheck grouping box if 0 dependent boxes are checked
- Check if all dependent boxes are checked
- Set indeterminate state for any other case
Key to this solution is that by binding event listeners from the NodeList allows easily grouping the checkboxes. Using onclick="listener"
would render that difficult.
Also it’s using the change
event, as you should always use events that are data-oriented and not interaction-oriented. Users could use the keyboard or other means to change a checkboxe’s state.
document.querySelectorAll('tbody tr').forEach(tr => {
// all the variables inside here are valid for one tr, establishing a checkbox group
const [checkAll, ...checkboxes] = tr.querySelectorAll('input[type="checkbox"]');
// propagate checked state down to dependent checkboxes
// we simply copy the grouping boxes’ state to the dependent boxes
checkAll.addEventListener('change', e => checkboxes.forEach(c => c.checked = e.currentTarget.checked));
// propagate checked state up depending on group state
checkboxes.forEach(c => c.addEventListener('change', e => {
// list all checked dependent checkboxes
const checked = checkboxes.filter(cb => cb.checked);
// and compare count with the whole group
if (checked.length === checkboxes.length) {
// all are checked
checkAll.checked = true;
checkAll.indeterminate = false;
} else if (checked.length === 0) {
// none is checked
checkAll.checked = false;
checkAll.indeterminate = false;
} else {
// some are checked
checkAll.indeterminate = true;
}
}));
});
<table>
<thead>
<tr>
<th style="text-align:center;">Select All</th>
<th style="text-align:center;">Student Name</th>
<th style="text-align:center;">On Time</th>
<th style="text-align:center;">Present</th>
<th style="text-align:center;">Contributing</th>
<th style="text-align:center;">Prep. Lesson</th>
</tr>
</thead>
<tbody>
<tr>
<div >
<!-- select all -->
<td style="text-align:center;"><input type="checkbox" aria-label="Check all criteria for Mickey Mouse"></td>
<!-- student -->
<td >
Mickey Mouse
</td>
<!-- on-time -->
<td style="text-align:center;"><input type="checkbox" id="on-time"></td>
<!-- Present -->
<td style="text-align:center;"><input type="checkbox" id="present"></td>
<!-- contributing -->
<td style="text-align:center;"><input type="checkbox" id="contribute"></td>
<!-- prepared lesson -->
<td style="text-align:center;"><input type="checkbox" id="prep-lesson"></td>
</tr>
<tr>
<div >
<!-- select all -->
<td style="text-align:center;"><input type="checkbox" aria-label="Check all criteria for Mini Mouse"></td>
<!-- student -->
<td >
Mini Mouse
</td>
<!-- on-time -->
<td style="text-align:center;"><input type="checkbox" id="on-time"></td>
<!-- Present -->
<td style="text-align:center;"><input type="checkbox" id="present"></td>
<!-- contributing -->
<td style="text-align:center;"><input type="checkbox" id="contribute"></td>
<!-- prepared lesson -->
<td style="text-align:center;"><input type="checkbox" id="prep-lesson"></td>
</tr>
</tbody>
</table>
Other Improvements
The code included an id="select-all"
inside a for loop. This is invalid, as IDs need to be unique. I removed it, because the suggested solution doesn’t need any IDs.
I also added a specific label for the first checkbox, to be sure. Assistive technology is often able to determine the labels for inputs inside a table from the table structure.
<td style="text-align:center;"><input type="checkbox" aria-label="Check all criteria for Mickey Mouse"></td>