Reputation: 6744
I've used the following codepen snippet to implement nested checkboxes into my HTML page. The original code only catered for one set of nested checkboxes so I've tried to expand to multiple. However, it seems to work for the last Main element in this case Main2
but not the previous ones.
function loadExpandableCheckboxes() {
var expandableCheckboxes = document.getElementsByClassName("expandable-checkbox");
for (var i = 0; i < expandableCheckboxes.length; i++) {
var checkboxMainOption = expandableCheckboxes[i].getElementsByClassName("expandable-checkbox-main-option")[0];
var checkboxSubOptions = expandableCheckboxes[i].getElementsByClassName("expandable-checkbox-sub-option");
for (var j = 0; j < checkboxSubOptions.length; j++) {
checkboxSubOptions[j].onclick = function() {
var checkedCount = 0;
for (var k = 0; k < checkboxSubOptions.length; k++) {
if (checkboxSubOptions[k].checked) {
checkedCount++;
}
}
checkboxMainOption.checked = checkedCount > 0;
checkboxMainOption.indeterminate = checkedCount > 0 && checkedCount < checkboxSubOptions.length;
}
}
checkboxMainOption.onclick = function() {
for (var j = 0; j < checkboxSubOptions.length; j++) {
checkboxSubOptions[j].checked = checkboxMainOption.checked;
}
}
}
}
onload=loadExpandableCheckboxes;
body {
color: #555;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
ul {
list-style: none;
}
li {
margin-top: 1em;
}
label {
font-weight: bold;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div class="checkbox-container">
<ul>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 1</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 3</label></li>
</ul>
</div>
</li>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 2</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 3</label></li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
Upvotes: 3
Views: 582
Reputation: 5516
If you don't mind using JQuery, you can shorten your JS here. Try the snippet below:
$(document).ready(function() {
$('.expandable-checkbox-main-option').change(function() {
$(this).parent().parent('div').find('.expandable-checkbox-sub-option').prop('checked', $(this).prop('checked'));
});
$('.expandable-checkbox-sub-option').change(function() {
var parentCheckboxDiv = $(this).parent().parent().parent().parent('div');
var parentCheckbox = parentCheckboxDiv.find('.expandable-checkbox-main-option');
var checkedSubOptions = parentCheckboxDiv.find('.expandable-checkbox-sub-option:checked');
var subOptions = parentCheckboxDiv.find('.expandable-checkbox-sub-option');
parentCheckbox.prop('indeterminate', checkedSubOptions.length && checkedSubOptions.length != subOptions.length);
parentCheckbox.prop('checked', checkedSubOptions.length == subOptions.length)
});
});
body {
color: #555;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
ul {
list-style: none;
}
li {
margin-top: 1em;
}
label {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="checkbox-container">
<ul>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 1</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 3</label></li>
</ul>
</div>
</li>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 2</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 3</label></li>
</ul>
</div>
</li>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 3 (5 options)</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 3 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 3 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 3 Sub 3</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 3 Sub 4</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 3 Sub 5</label></li>
</ul>
</div>
</li>
</ul>
</div>
Upvotes: 3
Reputation: 4113
This was a fun one. So basically to stop from overwriting the first set of checkboxes with the second set, you need to use a temporary scope to preserve the variables. In the snippet below, you'll see that I added an IFFE to your i
loop.
What this does is take the variable i
, pass it into the temporary function which calls itself immediately. Next time the loop runs, a new temporary function is "created", thus preserving the scope of the last one, and not overwriting the previous checkbox sets.
$(document).ready(function() {
//find the wrappers
var expandableCheckboxes = document.getElementsByClassName("expandable-checkbox");
//loop through the wrappers, count them one by one
for (var i = 0; i < expandableCheckboxes.length; i++) (function(i){
//find the the main
var checkboxMainOption = expandableCheckboxes[i].getElementsByClassName("expandable-checkbox-main-option")[0];
//find the subs
var checkboxSubOptions = expandableCheckboxes[i].getElementsByClassName("expandable-checkbox-sub-option");
//loop through subs
for (var k = 0; k < checkboxSubOptions.length; k++) {
//add listener fo each sub
checkboxSubOptions[k].onclick = function() {
var checkedCount = 0;
for (var j = 0; j < checkboxSubOptions.length; j++) {
if (checkboxSubOptions[j].checked) {
checkedCount++;
}
}
//mark appropriately
checkboxMainOption.checked = checkedCount > 0;
checkboxMainOption.indeterminate = checkedCount > 0 && checkedCount < checkboxSubOptions.length;
}
}
//add listener to main
checkboxMainOption.onclick = function() {
for (var j = 0; j < checkboxSubOptions.length; j++) {
checkboxSubOptions[j].checked = checkboxMainOption.checked;
}
}
})(i)
});
body {
color: #555;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
ul {
list-style: none;
}
li {
margin-top: 1em;
}
label {
font-weight: bold;
}
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div class="checkbox-container">
<ul>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 1</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 1 Sub 3</label></li>
</ul>
</div>
</li>
<li>
<div class="expandable-checkbox">
<label><input type="checkbox" class="expandable-checkbox-main-option">Main 2</label>
<ul>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 1</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 2</label></li>
<li><label><input type="checkbox" class="expandable-checkbox-sub-option">Main 2 Sub 3</label></li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
Upvotes: 4