JSFiddle - React, Tailwind, and code Playground
Nested Checklist | With Expanding / Collapsing Tree Nodes and Indeterminate Checkboxes
by skibulk
HTML
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<div class="nested-checklist">
<ul>
<li>
<fieldset>
<legend>Colors
<br>Colours</legend>
<label class="toggle-select">
<span>Select?</span>
<input type="checkbox" id="item-1" checked>
</label>
<label class="toggle-expand">
<span>Expand?</span>
<input type="checkbox" checked>
</label>
</fieldset>
<ul>
<li>
<fieldset>
<legend>Green</legend>
<label class="toggle-expand">
<span>Expand/Collapse</span>
<input type="checkbox" disabled>
</label>
<label class="toggle-select">
<span>Select/Deselect</span>
<input type="checkbox" id="item-1">
</label>
</fieldset>
</li>
<li>
<fieldset>
<legend>Red</legend>
<label class="toggle-expand">
<span>Expand/Collapse</span>
<input type="checkbox" disabled>
</label>
<label class="toggle-select">
<span>Select/Deselect</span>
<input type="checkbox" id="item-1">
</label>
</fieldset>
<ul>
<li>
<fieldset>
<legend>Burgundy</legend>
<label class="toggle-expand">
<span>Expand/Collapse</span>
<input type="checkbox" disabled>
</label>
<label class="toggle-select">
<span>Select/Deselect</span>
<input type="checkbox" id="item-1">
</label>
</fieldset>
</li>
<li>
<fieldset>
<legend>Crimson</legend>
<label class="toggle-expand">
<span>Expand/Collapse</span>
<input type="checkbox"...
CSS
* {
box-sizing: border-box;
}
body {
margin: 20px;
padding: 0;
font-family: sans-serif;
}
.nested-checklist {
font-size: 1rem;
line-height: 1.4rem;
}
.nested-checklist ul,
.nested-checklist fieldset,
.nested-checklist legend {
list-style: none;
margin: 0;
border: 0;
padding: 0;
}
.nested-checklist ul ul {
margin-left: 1rem;
}
.nested-checklist fieldset {
position: relative;
}
.nested-checklist legend {
margin-left: 2.3rem;
}
.nested-checklist label {
position: absolute;
top: 0;
width: 1rem;
height: 1rem;
background: lightgray;
}
.nested-checklist label.toggle-select {
left: 1rem;
}
.nested-checklist label span {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
.nested-checklist .state-expanded .toggle-expand {
background-image: url('');
}
.nested-checklist .state-collapsed .toggle-expand {
background-image: url('');
}
..nested-checklist .state-collapsed ul {
display: none;
}
.nested-checklist .state-selected .toggle-select {
background-image: url('');
}
.nested-checklist .state-unselected .toggle-select {
background-image: url('');
}
.nested-checklist .state-indeterminite .toggle-select {
background-image: url('');
}
.nested-checklist .state-disabled {
visibility: hidden;
}
JavaScript
var checkboxSelector = '.toggle-select input';
$(checkboxSelector).change(function(e) {
var checked = $(this).prop('checked'), // State
$parents = [].reverse.call($(this).parents('li')), // All Parent List Items
$parent = $parents.first();
[].shift.call($parents);
// Apply state to children
$parent.find(checkboxSelector).prop({
indeterminate: false,
checked: checked
});
$parents.each(function(index, parent) {
var $parent = $(parent),
$select = $parent.children('> fieldset ' + checkboxSelector),
$children = $parent.children('> ul ' + checkboxSelector),
childrenChecked = $children.prop('checked'),
indeterminate;
if (childrenChecked) {
$select.prop({
indeterminate: false,
checked: true
});
} else if (childrenChecked && !checked) {
indeterminate = $parent.find(checkboxSelector + ':checked').length > 0;
$select.prop({
indeterminate: indeterminate,
checked: checked
});
} else {
$select.prop({
indeterminate: true,
checked: false
});
return false;
}
});
});