Chris L
Chris L

Reputation: 115

Many checkbox groups and "select all"

I have 8 groups of checkboxes. Each with a "master" and between 8 and 12 sub-checkboxes and try to create a "un/select all" behaviour to each group.

I found this SO https://stackoverflow.com/a/25697142/9098325 but it only shows one group of checkboxes.

I made a fiddle with my not working solution here: https://jsfiddle.net/ex4rnq83/

$('.outer').on('click', function() {
  if (this.checked) {
    $('.inner').each(function() {
      this.checked = true;
    });
  } else {
    $('.inner').each(function() {
      this.checked = false;
    });
  }
});

$('.inner').on('click', function() {
  if ($(this).is(":checked")) {
    var isAllChecked = 0;

    $(".inner").each(function() {
      if (!this.checked)
        isAllChecked = 1;
    });

    if (isAllChecked == 0) {
      $(".outer").prop("checked", true);
    }
  } else {
    $(".outer").prop("checked", false);
  }
});
ul {
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <input type="checkbox" id="out1" class="outer">
    <label for="out1">Checkbox Out 1</label>
  </li>
  <ul>
    <li>
      <input type="checkbox" id="inner1" class="inner">
      <label for="out1">Checkbox Inner 1</label>
    </li>
    <li>
      <input type="checkbox" id="inner2" class="inner">
      <label for="out1">Checkbox Inner 2</label>
    </li>
    <li>
      <input type="checkbox" id="inner3" class="inner">
      <label for="out1">Checkbox Inner 3</label>
    </li>
  </ul>
  <li>
    <input type="checkbox" id="out2" class="outer">
    <label for="out1">Checkbox Out 2</label>
  </li>
  <ul>
    <li>
      <input type="checkbox" id="inner4" class="inner">
      <label for="out1">Checkbox Inner 4</label>
    </li>
    <li>
      <input type="checkbox" id="inner5" class="inner">
      <label for="out1">Checkbox Inner 5</label>
    </li>
  </ul>
</ul>

Upvotes: 2

Views: 1847

Answers (4)

FadedForEver
FadedForEver

Reputation: 144

Change this:

$('.outer').on('click', function() {
  if (this.checked) {
    $('.inner').each(function() {
      this.checked = true;
    });
  } else {
    $('.inner').each(function() {
      this.checked = false;
    });
  }
});

to this:

$('.outer').on('click',function(){
    if(this.checked){

        $(this).parent().next("ul").children('li').children('.inner').each(function(){
            this.checked = true;
        });
  }else{
        $(this).parent().next("ul").children('li').children('.inner').each(function(){
            this.checked = false;
    });
  }
});

jquery will first select parent then next ul and then all inputs with 'inner' class.

alse change end part of your js code to this:

    $('.inner').on('click',function(){
        if ($(this).is(":checked")) {
            var isAllChecked = 0;

            $(this).parent().parent().children('li').children('.inner').each(function() {
                if (!this.checked)
                    isAllChecked = 1;
            });

            if (isAllChecked == 0) {
                $(this).parent().parent().prev('li').children('.outer').prop("checked", true);
            }
        }
        else {
            console.log()
            $(this).parent().parent().prev('li').children('.outer').prop("checked", false);
        }

it will select outer input before your inner classes

Upvotes: 0

mplungjan
mplungjan

Reputation: 177965

Here is a working example

It toggles the children of the next UL and toggles the outer checkbox if all or less than all children are checked

I corrected the invalid HTML to have the ULs as children of the LI and not another UL

$('.outer').on('click', function() {
  var chk = this.checked;
  $(this).closest("li").find(".inner").each(function() {
    this.checked = chk;
  });
});

$('.inner').on('click', function() {
  var $parent = $(this).closest("ul"),
    $inner = $parent.find(".inner"),
    $checked = $parent.find(".inner:checked"),
    $outer = $parent.closest("li").find(".outer"),
    chk = $inner.length === $checked.length ? this.checked : false;
  $outer.prop("checked", chk)
});
ul {
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <input type="checkbox" id="out1" class="outer"><label for="out1">Checkbox Out 1</label>
    <ul>
      <li><input type="checkbox" id="inner1" class="inner"><label for="out1">Checkbox Inner 1</label></li>
      <li><input type="checkbox" id="inner2" class="inner"><label for="out1">Checkbox Inner 2</label></li>
      <li><input type="checkbox" id="inner3" class="inner"><label for="out1">Checkbox Inner 3</label></li>
    </ul>
  </li>
  <li>
    <input type="checkbox" id="out2" class="outer"><label for="out1">Checkbox Out 2</label>
    <ul>
      <li><input type="checkbox" id="inner4" class="inner"><label for="out1">Checkbox Inner 4</label></li>
      <li><input type="checkbox" id="inner5" class="inner"><label for="out1">Checkbox Inner 5</label></li>
    </ul>
  </li>
</ul>

Upvotes: 1

Rory McCrossan
Rory McCrossan

Reputation: 337570

Firstly if you correct the HTML so that the child ul are within the li of the parent, then this becomes much simpler. When a checkbox is checked you can easily traverse the DOM to find child checkboxes and set their checked state to match.

Similarly, you can get the checked state of sibling checkboxes. If they are all checked then any parent checkbox should be checked. Something like this:

$(':checkbox').on('change', function() {
  // check children
  $(this).closest('li').find(':checkbox').prop('checked', this.checked);

  // check parent if all children checked
  var $ul = $(this).closest('ul');
  var $siblings = $ul.find('> li > label > :checkbox');
  var allChecked = $siblings.length == $siblings.filter(':checked').length;
  $ul.closest('li').find('> label > :checkbox').prop('checked', allChecked);
});
ul {
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <label><input type="checkbox">Checkbox Out 1</label>
    <ul>
      <li>
        <label><input type="checkbox">Checkbox Inner 1</label>
      </li>
      <li>
        <label><input type="checkbox">Checkbox Inner 2</label>
      </li>
      <li>
        <label><input type="checkbox">Checkbox Inner 3</label>
      </li>
    </ul>
  </li>
  <li>
    <label><input type="checkbox">Checkbox Out 2</label>
    <ul>
      <li>
        <label><input type="checkbox">Checkbox Inner 4</label>
      </li>
      <li>
        <label><input type="checkbox">Checkbox Inner 5</label>
      </li>
    </ul>
  </li>
</ul>

Note this completely negates the need for any unique id attributes, as the logic relies on the DOM structure and the label elements have been moved to wrap the checkbox they are related to. The inner and outer class attributes have also been removed as they become redundant when nesting the ul/li correctly.

Upvotes: 4

callback
callback

Reputation: 4122

I modified your question with a working one.

Just replace $(".inner").each with $(this).parent().next().find('li input').each or another correct selector.

$('.outer').on('click', function() {
  if (this.checked) {
    $(this).parent().next().find('li input').each(function() {
      this.checked = true;
    });
  } else {
    $(this).parent().next().find('li input').each(function() {
      this.checked = false;
    });
  }
});

$('.inner').on('click', function() {
  if ($(this).is(":checked")) {
    var isAllChecked = 0;

    $(".inner").each(function() {
      if (!this.checked)
        isAllChecked = 1;
    });

    if (isAllChecked == 0) {
      $(".outer").prop("checked", true);
    }
  } else {
    $(".outer").prop("checked", false);
  }
});
ul {
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <input type="checkbox" id="out1" class="outer">
    <label for="out1">Checkbox Out 1</label>
  </li>
  <ul>
    <li>
      <input type="checkbox" id="inner1" class="inner">
      <label for="out1">Checkbox Inner 1</label>
    </li>
    <li>
      <input type="checkbox" id="inner2" class="inner">
      <label for="out1">Checkbox Inner 2</label>
    </li>
    <li>
      <input type="checkbox" id="inner3" class="inner">
      <label for="out1">Checkbox Inner 3</label>
    </li>
  </ul>
  <li>
    <input type="checkbox" id="out2" class="outer">
    <label for="out1">Checkbox Out 2</label>
  </li>
  <ul>
    <li>
      <input type="checkbox" id="inner4" class="inner">
      <label for="out1">Checkbox Inner 4</label>
    </li>
    <li>
      <input type="checkbox" id="inner5" class="inner">
      <label for="out1">Checkbox Inner 5</label>
    </li>
  </ul>
</ul>

Upvotes: 0

Related Questions