javascripthtmlhtml-listshierarchy

How to link checkbox-elements in a hierarchy-like unordered list?


I want my checkboxes to be linked: when I click on the level 2 checkbocks, I want all checkbockses be affected from starting from level 3. So far, I could come up only with this (I clicked on the level 2 checkbox): example

My template lookes like this:


<div id="checkbox-list" class="scrollable-list">
    <ul class="checkbox-outer-element">
        <li class="checkbox-inner-element"><input type="checkbox" value="_" id="_"><label for="_">Level 1</label>
            <ul class="checkbox-outer-element">
                <li class="checkbox-inner-element"><input type="checkbox" value="_" id="_"><label for="_">Level
                        2</label>
                    <ul class="checkbox-outer-element">
                        <li class="checkbox-inner-element"><input type="checkbox" value="71" id="71"><label
                                for="_">Level 3</label>
                            <ul class="checkbox-outer-element">
                                <li class="checkbox-inner-element"><input type="checkbox" value="71:0" id="71:0"><label
                                        for="71:0">Level 4</label></li>
                                <li class="checkbox-inner-element"><input type="checkbox" value="71:1" id="71:1"><label
                                        for="71:1">Level 4</label></li>
                                <li class="checkbox-inner-element"><input type="checkbox" value="71:2" id="71:2"><label
                                        for="71:2">Level 4</label></li>
                                <li class="checkbox-inner-element"><input type="checkbox" value="71:3" id="71:3"><label
                                        for="71:3">Level 4</label></li>
                            </ul>
                        </li>
                        <li class="checkbox-inner-element"><input type="checkbox" value="94" id="94"><label
                                for="_">Level 3</label>
                            <ul class="checkbox-outer-element">
                                <li class="checkbox-inner-element"><input type="checkbox" value="94:0" id="94:0"><label
                                        for="94:0">Level 4</label></li>
                                <li class="checkbox-inner-element"><input type="checkbox" value="94:1" id="94:1"><label
                                        for="94:1">Level 4</label></li>
                                <li class="checkbox-inner-element"><input type="checkbox" value="94:2" id="94:2"><label
                                        for="94:2">Level 4</label></li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
        <li class="checkbox-inner-element"><input type="checkbox" value="_" id="_"><label for="_">Level 2</label>
            <ul class="checkbox-outer-element">
                <li class="checkbox-inner-element"><input type="checkbox" value="_"
                        id="_"><label for="_">Level 3</label>
                    <ul class="checkbox-outer-element">
                        <li class="checkbox-inner-element"><input type="checkbox" value="_"
                                id="_"><label for="_">Level 4</label></li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>

I add event listeners to every checkbox I create with my script. My current JS code lookes like this:

checkbox.addEventListener('change', function () {
            var flag = this.checked
            var nestedCheckboxes = this.parentElement.querySelector('.checkbox-inner-element').querySelectorAll('ul li input[type="checkbox"]');
            nestedCheckboxes.forEach(function (checkbox) {
                checkbox.checked = flag;
                checkbox.dispatchEvent(new Event('change'));
            });
        })

I think the problem could be in that I have both <li> and <ul> tags on the same level, but I couldn't come up with the solution yet.


Solution

  • I would use query selectors to find all of the descendant checkboxes. This solution uses the input id, which I don't believe are allowed to start with a number.

    document.querySelectorAll("input[type=checkbox]").forEach(checkbox =>
      checkbox.addEventListener('change', function(e) {
        const activeCB = e.target;
        const flag = activeCB.checked;
        const descendants = activeCB.closest("li").querySelectorAll(`input[type=checkbox]`);
        
        descendants.forEach(d => d.checked = flag);
      })
    
    )
    <div id="checkbox-list" class="scrollable-list">
      <ul class="checkbox-outer-element">
        <li class="checkbox-inner-element"><input type="checkbox" value="_" id="L1a"><label for="_">Level 1</label>
          <ul class="checkbox-outer-element">
            <li class="checkbox-inner-element"><input type="checkbox" value="_" id="L2a"><label for="_">Level
                2</label>
              <ul class="checkbox-outer-element">
                <li class="checkbox-inner-element"><input type="checkbox" value="71" id="L3a"><label for="_">Level 3</label>
                  <ul class="checkbox-outer-element">
                    <li class="checkbox-inner-element"><input type="checkbox" value="71:0" id="L4a"><label for="71:0">Level 4</label></li>
                    <li class="checkbox-inner-element"><input type="checkbox" value="71:1" id="L4b"><label for="71:1">Level 4</label></li>
                    <li class="checkbox-inner-element"><input type="checkbox" value="71:2" id="L4c"><label for="71:2">Level 4</label></li>
                    <li class="checkbox-inner-element"><input type="checkbox" value="71:3" id="L4d"><label for="71:3">Level 4</label></li>
                  </ul>
                </li>
                <li class="checkbox-inner-element"><input type="checkbox" value="94" id="L3b"><label for="_">Level 3</label>
                  <ul class="checkbox-outer-element">
                    <li class="checkbox-inner-element"><input type="checkbox" value="94:0" id="L4e"><label for="94:0">Level 4</label></li>
                    <li class="checkbox-inner-element"><input type="checkbox" value="94:1" id="L4f"><label for="94:1">Level 4</label></li>
                    <li class="checkbox-inner-element"><input type="checkbox" value="94:2" id="L4g"><label for="94:2">Level 4</label></li>
                  </ul>
                </li>
              </ul>
            </li>
          </ul>
        </li>
        <li class="checkbox-inner-element"><input type="checkbox" value="_" id="L1b"><label for="_">Level 2</label>
          <ul class="checkbox-outer-element">
            <li class="checkbox-inner-element"><input type="checkbox" value="_" id="L2b"><label for="_">Level 3</label>
              <ul class="checkbox-outer-element">
                <li class="checkbox-inner-element"><input type="checkbox" value="_" id="L3c"><label for="_">Level 4</label></li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
    </div>