0

I have a form with multiple checkbox groups that each have a related "All" option, which when clicked, de-selects all the related checkboxes. The form also needs to to uncheck the "All" option when any of the related checkboxes are checked. Basically, the form can submit either the "All" option, any of the related options, or no options.

When any of the checkboxes is checked, the related label is made bold, including the "All" option.

I have written this logic in jQuery, however, I can't seem to get one piece to work properly - de-selecting the "All" option when one of the related options is checked. It works when the user clicks one of the related options, but it also UNCHECKS the "All" option after it was just checked.

I guess my question is, how can I determine what the "source" event was, so I can determine whether or not to run that one piece of code?

Demo with "de-select 'All'" commented out: http://jsfiddle.net/8Ctvd/

JS:

$(function () {
    $(".all").change(function () {
        if ($(this).is(":checked")) $("input[rel='" + $(this).attr("rel") + "']:checked").not(".all").removeAttr("checked").change();
    });

    $("input[type='checkbox']").change(function (e) {
        if ($(this).is(":checked")) $("label[for='" + $(this).attr("id") + "']").css("font-weight", "bold");
        else {
            $("label[for='" + $(this).attr("id") + "']").css("font-weight", "normal");
            /* THIS UNCHECKS THE ALL EVERYTIME
            if  ($(this).not(".all"))
                $("input.all[rel='" + $(this).attr("rel") + "']").removeAttr('checked').change();
            */
        }
    });
});

HTML:

<INPUT id=items_All class="checkbox all" name=items value=ALL type=checkbox rel="items">
<LABEL class="items checkbox-label" for=items_All>All</LABEL>
<INPUT id=items_1 class=checkbox name=items value=1 type=checkbox rel="items">
<LABEL class="items checkbox-label" for=items_1>Item 1</LABEL>
<INPUT id=items_2 class=checkbox name=items value=2 type=checkbox rel="items">
<LABEL class="items checkbox-label" for=items_2>Item 2</LABEL>
<INPUT id=items_3 class=checkbox name=items value=3 type=checkbox rel="items">
<LABEL class="items checkbox-label" for=items_3>Item 3</LABEL>
<br />
<INPUT id=widgets_All class="checkbox all" name=widgets value=ALL type=checkbox rel="widgets">
<LABEL class="widgets checkbox-label" for=widgets_All>All</LABEL>
<INPUT id=widgets_1 class=checkbox name=widgets value=1 type=checkbox rel="widgets">
<LABEL class="widgets checkbox-label" for=widgets_1>Widget 1</LABEL>
<INPUT id=widgets_2 class=checkbox name=widgets value=2 type=checkbox rel="widgets">
<LABEL class="widgets checkbox-label" for=widgets_2>Widget 2</LABEL>
<INPUT id=widgets_3 class=checkbox name=widgets value=3 type=checkbox rel="widgets">
<LABEL class="widgets checkbox-label" for=widgets_3>Widget 3</LABEL>
Eric Belair
  • 10,574
  • 13
  • 75
  • 116

2 Answers2

0

Edit

I have tweaked the JS to fit the requirements I misunderstood. Part of the issue was that your logic was a bit off, and could be significantly simplified. Separating the formatting logic to a separate function lets you look at the formatting separately from the checking.

$("input[type='checkbox']").change(function (e) {
    var $o = $(this);
    if ($o.hasClass("all")) {
        //all box was checked, uncheck others
        if ($o.prop("checked"))
            $("input[rel='" + $o.attr("rel") + "']").not(this).prop("checked", false);
    } else {
        // other box was checked, uncheck all
        if ($o.prop("checked")) {
            $("input.all[rel='" + $o.attr("rel") + "']").prop("checked", false);
        }
    }
    fixMarkup();
});

See here for full example: http://jsfiddle.net/8Ctvd/3/

Original Answer

You are using attr, which will give you unpredictable results in your case. Also, using .is is slower than .prop. You should use .prop in jQuery, which gets translated to a boolean and reflects the current state of the tag instead of its initial markup. See this SO answer for reference.

Also, you call .change() on the final not('.all') which results in the change event firing on the "All" checkbox. Check this updated fiddle.

http://jsfiddle.net/8Ctvd/1/

Community
  • 1
  • 1
Michael Dunlap
  • 4,300
  • 25
  • 36
  • Thanks for your suggestions. Unfortunately, this doesn't meet my requirements. I need the related checkboxes to be UN-checked when the "All" option is selected. – Eric Belair Apr 10 '13 at 16:56
  • Wait. What? You want the "All" checkbox to be the OPPOSITE of the state of its related checkboxes? – Michael Dunlap Apr 10 '13 at 17:00
  • Yes. "All" is a valid value that can be submitted by the form. The database saves either the value "ALL", or the list of selected options, but not both. Crappy design, I know, but it's what I have to work with. – Eric Belair Apr 10 '13 at 17:01
  • @DigitalID still doesn't work to uncheck the related checkboxes when checking "All". I have added my working solution - using the click event on the related checkboxes. – Eric Belair Apr 10 '13 at 17:55
  • @EricBelair Strange. Works great for me on Chrome. – Michael Dunlap Apr 10 '13 at 18:57
0

I figured this out, by using the "click" event for the related checkboxes. That way, a change event won't trigger it.

JS:

$('.all').change(function(){
    if ($(this).is(':checked'))
        $('input[rel="' + $(this).prop('rel') + '"]:checked').not('.all').removeProp('checked').change();
});

$('input[type="checkbox"]').change(function(e){
    var $label = $('label[for="' + $(this).prop('id') + '"]');

    if ($(this).is(':checked'))
        $label.css('font-weight', 'bold');
    else
        $label.css('font-weight', 'normal');
});

$('input[type="checkbox"]').not('.all').click(function(e){
    var $allCheckbox =
        $('input.all[rel="' + $(this).prop('rel') + '"]');

    if ($allCheckbox.is(':checked'))
        $allCheckbox.removeProp('checked').change();
});
Eric Belair
  • 10,574
  • 13
  • 75
  • 116
  • Don't use `removeProp`. That literally removes the property from the DOM element. Use `.prop("checked", false)` to un-check the box. – Michael Dunlap Apr 10 '13 at 18:58