Nested parent/child checkboxes - working solution need adjustment help for Bootstrap

Sam picture Sam · Dec 3, 2013 · Viewed 16.3k times · Source

I have been looking for a 'complete' solution to nesting parent child checkboxes that change state correctly based on a hierarchy.

Most 'solutions' do not work or only work to one level. They also require you to name the checkboxes in a particular way.

This Stack Overflow discussion covers the main points but also provide a good solution discovered by Rory here.

I have tested it within my development project and it works perfectly standalone. However, I am using Bootstrap 2.x and for checkboxes

I have a JSFiddle which shows the working example code, then my version with a disabled parent checkbox and then the bootsrap code version which does not work.

<!DOCTYPE html>
<body>
<!-- Raw working example from site http://css-tricks.com/indeterminate-checkboxes/ -->

<b>Raw working example</b>
<p>
<ul>
    <li>
        <input type="checkbox" name="tall" id="tall">
        <label for="tall">Tall Things</label>
        <ul>
            <li>
                <input type="checkbox" name="tall-1" id="tall-1">
                <label for="tall-1">Buildings</label>
            </li>
            <li>
                <input type="checkbox" name="tall-2" id="tall-2">
                <label for="tall-2">Giants</label>
                <ul>
                    <li>
                        <input type="checkbox" name="tall-2-1" id="tall-2-1">
                        <label for="tall-2-1">Andre</label>
                    </li>
                    <li>
                        <input type="checkbox" name="tall-2-2" id="tall-2-2">
                        <label for="tall-2-2">Paul Bunyan</label>
                        <ul>
                            <li>
                                <input type="checkbox" name="tall-2-2-1" id="tall-2-2-1">
                                <label for="tall-2-2-1">Son</label>
                            </li>
                            <li>
                                <input type="checkbox" name="tall-2-2-2" id="tall-2-2-2">
                                <label for="tall-2-2-2">Daughter</label>
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
            <li>
                <input type="checkbox" name="tall-3" id="tall-3">
                <label for="tall-3">Two sandwiches</label>
            </li>
        </ul>
    </li>
    <li>
        <input type="checkbox" name="short" id="short">
        <label for="short">Short Things</label>
        <ul>
            <li>
                <input type="checkbox" name="short-1" id="short-1">
                <label for="short-1">Smurfs</label>
            </li>
            <li>
                <input type="checkbox" name="short-2" id="short-2">
                <label for="short-2">Mushrooms</label>
            </li>
            <li>
                <input type="checkbox" name="short-3" id="short-3">
                <label for="short-3">One Sandwich</label>
            </li>
        </ul>
    </li>
</ul>

<hr>

<!-- Non Bootstrap Example -->
<b>My initial code example - Is Working</b>
<p>
<ul>
    <li>

        <input type="checkbox" name="" value="" disabled><strong>Ford</strong>

        <ul>
            <li>

                <input type="checkbox" name="" value="">Fiesta</label>
            </li>
            <li>

                <input type="checkbox" name="" value="">Focus</label>
            </li>
            <li>

                <input type="checkbox" name="" value="">Mondeo</label>
            </li>
        </ul>
    </li>
    <li>

        <input type="checkbox" name="" value="" disabled><strong>Vauxhall</strong>


        <ul>
            <li>

                    <input type="checkbox" name="" value="">Corsa</label>
            </li>
            <li>

                    <input type="checkbox" name="" value="">Astra</label>
            </li>
            <li>

                    <input type="checkbox" name="" value="">Vectra</label>
            </li>
        </ul>
    </li>
</ul>

            <hr>

<!-- Bootstrap Example -->

<b>Bootstrap based code example - Not Working</b>
<p>

<ul>
    <li>
        <label class="checkbox">
            <input type="checkbox" name="" value="" disabled><strong>Ford</strong>

        </label>
        <ul>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Fiesta</label>
            </li>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Focus</label>
            </li>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Mondeo</label>
            </li>
        </ul>
    </li>
    <li>
        <label class="checkbox">
            <input type="checkbox" name="" value="" disabled><strong>Vauxhall</strong>

        </label>
        <ul>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Corsa</label>
            </li>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Astra</label>
            </li>
            <li>
                <label class="checkbox">
                    <input type="checkbox" name="" value="">Vectra</label>
            </li>
        </ul>
    </li>
</ul>
$(function () {
  // Apparently click is better chan change? Cuz IE?
  $('input[type="checkbox"]').change(function (e) {
      var checked = $(this).prop("checked"),
          container = $(this).parent(),
          siblings = container.siblings();

      container.find('input[type="checkbox"]').prop({
          indeterminate: false,
          checked: checked
      });

      function checkSiblings(el) {
          var parent = el.parent().parent(),
              all = true;

          el.siblings().each(function () {
              return all = ($(this).children('input[type="checkbox"]').prop("checked") === checked);
          });

          if (all && checked) {
              parent.children('input[type="checkbox"]').prop({
                  indeterminate: false,
                  checked: checked
              });
              checkSiblings(parent);
          } else if (all && !checked) {
              parent.children('input[type="checkbox"]').prop("checked", checked);
              parent.children('input[type="checkbox"]').prop("indeterminate", (parent.find('input[type="checkbox"]:checked').length > 0));
              checkSiblings(parent);
          } else {
              el.parents("li").children('input[type="checkbox"]').prop({
                  indeterminate: true,
                  checked: false
              }); 
          } 
      } 

      checkSiblings(container); 
  }); 
});

My understanding is that the code needs to be changed somewhere to use parents or closest. Can someone who is a much better code please help identify where the change needs to happen to get the Bootstrap version working.

Answer

Abraham Uribe picture Abraham Uribe · Dec 3, 2013

you can try something like this

  $(function () {
  $('input[type="checkbox"]').change(function (e) {
      var checked = $(this).prop("checked"),
          container = $(this).closest("li"),//get closest li instead of parent
          siblings = container.siblings();
      container.find('input[type="checkbox"]').prop({
          indeterminate: false,
          checked: checked
      });

      function checkSiblings(el) {
          var parent = el.parent().parent(),
              all = true,
              parentcheck=parent.children("label");//get the label that contains the disabled checkbox
          el.siblings().each(function () {
              return all = ($(this).find('input[type="checkbox"]').prop("checked") === checked);
          });
          //use parentcheck instead of parent to get the children checkbox
          if (all && checked) {
              parentcheck.children('input[type="checkbox"]').prop({
                  indeterminate: false,
                  checked: checked
              });
              checkSiblings(parent);
          } else if (all && !checked) {
              parentcheck.children('input[type="checkbox"]').prop("checked", checked);
              parentcheck.children('input[type="checkbox"]').prop("indeterminate", (parent.find('input[type="checkbox"]:checked').length > 0));
              checkSiblings(parent);
          } else {
             parentcheck.children('input[type="checkbox"]').prop({
                  indeterminate: true,
                  checked: false
              });
          }
      }
      checkSiblings(container);
  });
});    

http://jsfiddle.net/Mvs87/2/