0

As continuation of my previous question dealing with checkboxes in MVC. I've been doing just like this from this question but it seems that it's not working because of the hidden field created by the CheckBoxFor of MVC. Any idea why? Here's my fiddle showing the actual html

My actual html from CheckboxFor:

<form role="form">

                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="1" name="[0].IsSelected" type="checkbox" value="true"><input name="[0].IsSelected" type="hidden" value="false">
                        <label for="1"><b>Faa</b></label>

                    </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="2" name="[0].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[0].SubsCategories[0].IsSelected" type="hidden" value="false">
                            <label for="2">Faa1</label>

                        </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="3" name="[1].IsSelected" type="checkbox" value="true"><input name="[1].IsSelected" type="hidden" value="false">
                        <label for="3"><b>Fee</b></label>

                    </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="4" name="[1].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[1].SubsCategories[0].IsSelected" type="hidden" value="false">
                            <label for="4">Fee1</label>

                        </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="5" name="[1].SubsCategories[1].IsSelected" type="checkbox" value="true"><input name="[1].SubsCategories[1].IsSelected" type="hidden" value="false">
                            <label for="5">Fee2</label>

                        </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="6" name="[2].IsSelected" type="checkbox" value="true"><input name="[2].IsSelected" type="hidden" value="false">
                        <label for="6"><b>Fii</b></label>

                    </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="7" name="[2].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[2].SubsCategories[0].IsSelected" type="hidden" value="false">
                            <label for="7">Fii1</label>

                        </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="8" name="[2].SubsCategories[1].IsSelected" type="checkbox" value="true"><input name="[2].SubsCategories[1].IsSelected" type="hidden" value="false">
                            <label for="8">Fii2</label>

                        </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="9" name="[3].IsSelected" type="checkbox" value="true"><input name="[3].IsSelected" type="hidden" value="false">
                        <label for="9"><b>Foo</b></label>

                    </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="10" name="[3].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[3].SubsCategories[0].IsSelected" type="hidden" value="false">
                            <label for="10">Foo1</label>

                        </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="11" name="[4].IsSelected" type="checkbox" value="true"><input name="[4].IsSelected" type="hidden" value="false">
                        <label for="11"><b>Fuu</b></label>

                    </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="12" name="[5].IsSelected" type="checkbox" value="true"><input name="[5].IsSelected" type="hidden" value="false">
                        <label for="12"><b>Bee</b></label>

                    </li>
                </ul>
                <ul class="col-xs-3">
                    <li class="checkbox checkbox-info">
                        <input id="13" name="[6].IsSelected" type="checkbox" value="true"><input name="[6].IsSelected" type="hidden" value="false">
                        <label for="13"><b>Bii</b></label>

                    </li>
                        <li class="checkbox checkbox-info col-xs-offset-1">
                            <input id="14" name="[6].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[6].SubsCategories[0].IsSelected" type="hidden" value="false">
                            <label for="14">Bii1</label>

                        </li>
                </ul>

</form>

I'm trying to check the header and all the child checkboxes will be checked and if all the child checkboxes are being check, the parent checkbox will be checked and vice versa.

The js I'm trying to work on:

$('li :checkbox').on('click', function () {
    var $chk = $(this),
        $li = $chk.closest('li'),
        $ul, $parent;
    if ($li.has('ul')) {
        $li.find(':checkbox').not(this).prop('checked', this.checked)
    }
    do {
        $ul = $li.parent();
        $parent = $ul.siblings(':checkbox');
        if ($chk.is(':checked')) {
            $parent.prop('checked', $ul.has(':checkbox:not(:checked)').length == 0)
        } else {
            $parent.prop('checked', false)
        }
        $chk = $parent;
        $li = $chk.closest('li');
    } while ($ul.is(':not(.someclass)'));
});

UPDATE: I made it working with the help of the answers below. Here's the working answer in fiddle. I did follow the suggestions by putting the flag whether the checkbox is parent or not. For safety, I also added child-li class so that I'll not use the bootstrap col-xs-offset-1, that might change anytime. Thank you very much!

Community
  • 1
  • 1
naru
  • 129
  • 4
  • 20
  • 1
    You need to show the relevant code in the question, not as a link –  Aug 14 '15 at 05:17
  • You have not explained what your trying to do with the script –  Aug 14 '15 at 05:33
  • 1
    You making this very difficult because its hard to identify which is a parent and which is a child checkbox. You will find it far easier if you add some class names to the elements or put the child checkboxes inside its own `ul` element –  Aug 14 '15 at 05:54
  • You can ignore the extra hidden input field - you should not be (re-)setting this value at any time. It's used to post the value of the checkbox to the controller. If a checkbox is unticked, it is not included in the form, so the hidden input includes an explicit false for that checkbox. – freedomn-m Aug 14 '15 at 07:45
  • I put the js code provided into the jsfiddle and there's a lot of confusing issues with it! eg `parent=ul.siblings(":checkbox")` - the ul won't have any "siblings" that are checkboxes (and why call this 'parent'?) `chk=$(this)` and then `chk=$parent`? – freedomn-m Aug 14 '15 at 07:53

2 Answers2

1

This works with multiple levels of nesting:

  $(document).ready(function(){
    $('input[type=checkbox]').on('click', function () {
        var currentName = $(this).attr("name")
        var parrentVal = $(this).is(":checked"); 
        var childrenPrefix = currentName.substring( 0, currentName.lastIndexOf( "IsSelected" ) );
        var childrenInputs = $('input[type=checkbox][name^="'+childrenPrefix+'"]')
        childrenInputs.each(function(){           
           $(this).prop("checked", parrentVal);
        })
        var suffixIndex = currentName.lastIndexOf('.SubsCategories');
        if(suffixIndex > -1)
        {
            var parntName  = currentName.substring( 0, suffixIndex);
            var siblingsAndChildren = $('input[type=checkbox][name^="'+parntName+'.SubsCategories"]')
            if(siblingsAndChildren.length > 0)
            {
                var selectedSiblingsAndChildren = $(siblingsAndChildren).filter( ":checked" );
                $('input[type=checkbox][name="'+parntName+'.IsSelected"]').prop("checked", selectedSiblingsAndChildren.length === siblingsAndChildren.length);
            }
        }

    })
})

Working fiddle

Alex Art.
  • 8,711
  • 3
  • 29
  • 47
  • It seems that it's not working if the parent `ul` was clicked. It doesn't check the child checkbox. – naru Aug 15 '15 at 11:27
  • Attached code (similar to the one that I added in fiddle) tested in 3 Chrome, Mozila and IE. – Alex Art. Aug 15 '15 at 11:32
  • It's working :) Thank you, for future need I might use the added level of checkbox. For now my requirement is only two level of checkbox – naru Aug 15 '15 at 11:43
1

First, (as pointed out in the comments), it's much easier if you indicate somewhere which is a parent and which is a child checkbox. In the provided code, this can sort of be implied by as it looks like the child lis have class 'col-xs-offset-1' - but this is a layout class so should not be used for semantic meaning as the layout may change.

So, with the current html, you can determine if a checkbox is the parent or not by doing:

var isparent = !$(this).closest("li").is(".col-xs-offset-1");

As stated above, we recommend not doing this - just working with what's been given. Instead you should add classes (in the example below 'parentCheckbox' and 'childCheckbox' have been added)

<ul class="col-xs-3">
    <li class="checkbox checkbox-info">
        <input class='parentCheckbox' id="1" name="[0].IsSelected" type="checkbox" value="true"><input name="[0].IsSelected" type="hidden" value="false">
        <label for="1"><b>Faa</b></label>

    </li>
    <li class="checkbox checkbox-info col-xs-offset-1">
        <input class='childCheckbox' id="2" name="[0].SubsCategories[0].IsSelected" type="checkbox" value="true"><input name="[0].SubsCategories[0].IsSelected" type="hidden" value="false">
        <label for="2">Faa1</label>
    </li>
</ul>

then use:

var isparent = $(this).is(".parentCheckbox");

(of course, you only need to add a class to the parents, but it makes it easier later to explicitly state parent/child and you could add them to the li if that makes more sense when building with MVC/Razor).

Then handle parent and child checkboxes in separate routines.

When the parent is ticked, update all checkboxes except the parent to match the parent:

var checked = chk.is(":checked");
chk.closest("ul")
   .find(":checkbox")
   .not(chk)
   .prop('checked', checked);

When a child is ticked, check if all the child checkboxes are ticked and update the parent:

var parent = chk.closest("ul")
                .find("li:not(.col-xs-offset-1)")
                .find(":checkbox");
var children = chk.closest("ul")
                  .find("li.col-xs-offset-1")
                  .find(":checkbox");

if (children.filter(":checked").length == children.length)
    parent.prop("checked", true);
else
    parent.prop("checked", false);

Here's the above added to your html jsfiddle: http://jsfiddle.net/3xczqogy/6/

Finally, laying out html to indicate parent/child is not how html works - it might help you work out what's going on, but there are better ways to do this. (In the question code, the "child" (actually sibling) lis are indented with spaces and no other tag/node).

freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • Thank you so much, I followed your suggestion in putting a flag whether it's parent or child checkbox. Thank you also for working on in my existing html though I changed it already as you said it's much better in putting a flag in parent and child chekbox. – naru Aug 15 '15 at 11:27