Here's a nice example of creating a tree view in pure HTML and CSS - you don't even need any JavaScript:
Tree views in CSS[
^]
Adapting that to add checkboxes to each item is fairly simple:
<ul class="tree">
<li>
<details open>
<summary><input type="checkbox" /> Giant planets</summary>
<ul>
<li>
<details>
<summary><input type="checkbox" /> Gas giants</summary>
<ul>
<li><input type="checkbox" name="planet" value="Jupiter" /> Jupiter</li>
<li><input type="checkbox" name="planet" value="Saturn" /> Saturn</li>
</ul>
</details>
</li>
<li>
<details>
<summary><input type="checkbox" /> Ice giants</summary>
<ul>
<li><input type="checkbox" name="planet" value="Uranus" /> Uranus</li>
<li><input type="checkbox" name="planet" value="Neptune" /> Neptune</li>
</ul>
</details>
</li>
</ul>
</details>
</li>
</ul>
document.addEventListener("click", e => {
const { target } = e;
if (!target.matches(".tree input[type='checkbox']")) { return; }
if (target.matches("summary input")) {
const group = target.closest("details");
group.querySelectorAll("input[type='checkbox']").forEach(el => el.checked = target.checked);
}
const tree = target.closest(".tree");
tree.querySelectorAll("details").forEach(group => {
const header = group.querySelector("summary input[type='checkbox']");
header.checked = group.querySelector("li > input[type='checkbox']:not(:checked)") === null;
});
});
Demo[
^]
Only the checkboxes on the "leaf" nodes have names and values, so the submitted data will include the checked leaf nodes,
not the header nodes. If that's not what you want, then you can add appropriate names and values to the header nodes.