Search code examples
jquery-selectorstogglecheckboxchildrennested-lists

Using jQuery to Independently Toggle a Nested List of Checkboxes


<ul>
   <li><input type="checkbox" name="item-1" />Item 1
    <ul class="sublist">
       <li><input type="checkbox" name="item-1-sublist" />Item 1-1</li>
       <li><input type="checkbox" name="item-1-sublist" />Item 1-2</li>
       <li><input type="checkbox" name="item-1-sublist" />Item 1-3</li>
     </ul>
   </li>
   <li>
     <input type="checkbox" name="item-2" />Item 2
     <ul class="sublist">
        <li><input type="checkbox" name="item-2-sublist" />Item 2-1</li>
        <li><input type="checkbox" name="item-2-sublist" />Item 2-2</li>
        <li><input type="checkbox" name="item-2-sublist" />Item 2-3</li>
      </ul>
    </li>
</ul>

Considering the code above, if the user clicks on one of the main-level checkboxes (ie, Item 1 or Item 2), how would I go about toggling all checkboxes in the neighboring list with the sublist class value? I don't want to use any other class or id values because I'll have several types of sublists that need to be toggled independently; I don't want to duplicate chunks of code for each set of class/id values.

I've had the toggle-all-children-of neighbor but I've had to scrap that code.


Solution

  • You could use the $.parent() functions, like so (demo):

    $('input[name="item-1"], input[name="item-2"]').change(function() {
      var checkbox = $(this), checked = checkbox.is(':checked');
      $('ul.sublist input[type="checkbox"]', checkbox.parent()).attr('checked', checked);
    })
    

    This code could be a little cleaner if your child check boxes had a name that didn't start with the same string as their parents like so:

    <ul>
       <li><input type="checkbox" name="item-1" />Item 1
        <ul class="sublist">
           <li><input type="checkbox" name="sublist-item-1-1" />Item 1-1</li>
           <li><input type="checkbox" name="sublist-item-1-2" />Item 1-2</li>
           <li><input type="checkbox" name="sublist-item-1-3" />Item 1-3</li>
         </ul>
       </li>
       <li>
         <input type="checkbox" name="item-2" />Item 2
         <ul class="sublist">
            <li><input type="checkbox" name="sublist-item-2-1" />Item 2-1</li>
            <li><input type="checkbox" name="sublist-item-2-2" />Item 2-2</li>
            <li><input type="checkbox" name="sublist-item-2-3" />Item 2-3</li>
          </ul>
        </li>
    </ul>
    

    and:

    $('input[name^="item-"]').change(function() {
      var checkbox = $(this), checked = checkbox.is(':checked');
      $('ul.sublist input[type="checkbox"]', checkbox.parent()).attr('checked', checked);
    })
    

    This uses the pseudo selector ^= which means starts with. You could use that in your original HTML because your child selectors all start with the name string.

    ------Edit------

    For a better result use prop instead of attr, sometimes the attr works only the first time you change the values. So it would be:

    $('input[name^="item-"]').change(function() {
      var checkbox = $(this), checked = checkbox.is(':checked');
      $('ul.sublist input[type="checkbox"]', checkbox.parent()).prop('checked', checked);
    })