2

I'm using jQuery to help select a some matching checkbox elements in a form. The form is created dynamically, but here is a stripped down version of it that illustrated my point:

http://jsfiddle.net/phinor/qj7uj3rc/

This is the relevant JavaScript:

$(document).ready (function () {
    $('.lessoncount').on ('click', tickAlike);
});

function tickAlike ()
{
    var classes = $(this).attr('data-classes');

    if ($(this).siblings ('.tick_' + classes).attr ('checked'))
    {
        console.log ('ticking');
        $('.tick_' + classes).removeAttr ('checked');
    }
    else
    {
        console.log ('unticking');
        $('.tick_' + classes).attr ('checked', true);
    }
}

The idea is that checkboxes can be individually selected or, if the "label" is clicked on, all checkboxes with the same class will be selected or deselected to match the box next to it. Because I want two possible behaviours depending on whether they click on the box or the label, I cannot use the <label> tag. As a follow on from that, because of the independent selection, "toggling" is not viable.

This seems to work for one cycle of "ticks" and "unticks" and then stops. If I inspect the checkbox element in Chrome, I can see that the "checked" attribute is being added and removed appropriately, but the browser view is no longer updated and the ticks do not show.

This is consistent in the lastest versions of Chrome, Firefox and IE, so I'm guessing that I'm doing something wrong!

Philip
  • 3,689
  • 3
  • 24
  • 35

4 Answers4

0

You should be using .prop() instead of .attr() when modifying boolean attributes, e.g. checked, disabled, readonly, selected and etc. Using .removeAttr() or .removeProp() will cause the attribute to be removed forever, and cannot be further manipulated. To understand the difference between .prop() and .attr(), there is actually a very good question (with a well-structured answer) asked on SO before.

To programmatically check a checkbox, simply use $selector.prop('checked',true). To uncheck, use $selector.prop('checked',false).

Also, to check if a checkbox is checked (no pun intended), you can use the .is() method to do so, i.e. $selector.is(':checked').


$(document).ready(function () {
    $('.lessoncount').on('click', tickAlike);
});

function tickAlike ()
{
    var classes = $(this).attr('data-classes');
    if ($(this).siblings('.tick_' + classes).is(':checked'))
    {
        console.log('ticking');
        $('.tick_' + classes).prop('checked', false);
    }
    else
    {
        console.log('unticking');
        $('.tick_' + classes).prop('checked', true);
    }
}

A working demo: http://jsfiddle.net/teddyrised/qj7uj3rc/5/


Even better: I can see that the <span> element containing the description for each checkbox is semantically unhinged from its accompanying checkbox, i.e. there is no way to give context to each checkbox. The <label> element is created for this purpose.

You might still want to style your descriptors independently, so we can keep them wrapped in <span>, but we can wrap all the content within each <td> with the <label> element, so that a click on both the text and the checkbox will trigger the same response.

A revised markup for the rows in the table body would look something like this:

<tr>
    <td>1-Mo</td>
    <td><label for="lesson_1_1"><input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024"/> <span data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">(7)</span></label></td>
    <td><label for="lesson_1_2"><input type="checkbox" name="lesson[1][2]" value="1" id="lesson_1_2" class="tick_1582_1583_1584_1585_1586_1587"/> <span data-classes="1582_1583_1584_1585_1586_1587" class="lessoncount">(6)</span></label></td>
    <td><label for="lesson_1_3"><input type="checkbox" name="lesson[1][3]" value="1" id="lesson_1_3" class="tick_1658_1673_1684_1700_1706_1736_1737_1769"/> <span data-classes="1658_1673_1684_1700_1706_1736_1737_1769" class="lessoncount">(8)</span></label></td>
    <td><label for="lesson_1_4"><input type="checkbox" name="lesson[1][4]" value="1" id="lesson_1_4" class="tick_1602_1603_1604_1605_1618_1628"/> <span data-classes="1602_1603_1604_1605_1618_1628" class="lessoncount">(6)</span></label></td>
    <td><label for="lesson_1_5"><input type="checkbox" name="lesson[1][5]" value="1" id="lesson_1_5" class="tick_"/> <span>(0)</span></label></td>
    <td><label for="lesson_1_6"><input type="checkbox" name="lesson[1][6]" value="1" id="lesson_1_6" class="tick_1659_1674_1701_1738_1755_1756"/> <span data-classes="1659_1674_1701_1738_1755_1756" class="lessoncount">(6)</span></label></td>
    <td><label for="lesson_1_7"><input type="checkbox" name="lesson[1][7]" value="1" id="lesson_1_7" class="tick_1719_1720_1721_1722_1723_2003"/> <span data-classes="1719_1720_1721_1722_1723_2003" class="lessoncount">(6)</span></label></td>
    <td>detail | clear</td>
  </tr>

With a slightly modified script to listen to the click even on the wrapping <label> element instead of a text span:

$(document).ready(function () {
    $('table tbody label').on('click', tickAlike);
});

function tickAlike ()
{
    var classes = $(this).find('span.lessoncount').attr('data-classes');
    if ($(this).find('input[type="checkbox"]').is(':checked'))
    {
        console.log('ticking');
        $('.tick_' + classes).prop('checked', false);
    }
    else
    {
        console.log('unticking');
        $('.tick_' + classes).prop('checked', true);
    }
}

See alternative working demo: http://jsfiddle.net/teddyrised/pb68gk2n/

Community
  • 1
  • 1
Terry
  • 63,248
  • 15
  • 96
  • 118
  • Thanks, Terry. Part of my original question was that the clicking on the checkbox and the text should produce _different_ responses, hence not going for the ` – Philip Apr 11 '15 at 07:12
0

The problem is that you should not manipulate with attributes when you want to toggle properties checked.

Here is updated and simplified code:

$(document).ready(function () {
    $('.lessoncount').on('click', tickAlike);
});

function tickAlike() {
    var classes = $(this).data('classes');
    var $checkbox = $(this).siblings('.tick_' + classes).click();
    $('.tick_' + classes).prop('checked', $checkbox.is(':checked'));
}

Demo: http://jsfiddle.net/qj7uj3rc/6/

However this is still not ideal: note how clicking on checkbox itself does nothing. So it can be improved. You need to use label instead of span and wrap checkboxes into those labels. So HTML should be this:

<label data-classes="1645_1646_1647_1648_1649_1650_2024" class="lessoncount">
    <input type="checkbox" name="lesson[1][1]" value="1" id="lesson_1_1" class="tick_1645_1646_1647_1648_1649_1650_2024" /> (7)
</label>

and after that bind onchange event on checkboxes instead of spans:

$(document).ready(function () {
    $('.lessoncount :checkbox').on('change', tickAlike);
});

function tickAlike() {
    var classes = $(this).parent().data('classes');
    $('.tick_' + classes).not(this).prop('checked', this.checked);
}
dfsq
  • 191,768
  • 25
  • 236
  • 258
0

Use "is(':checked')" for the conditional and use ".prop ('checked', false)" and ".prop ('checked', true)" in order to make changes in the check input.

Here is the link https://jsfiddle.net/qj7uj3rc/7/

if ($(this).siblings ('.tick_' + classes).is(':checked'))
{
    console.log ('ticking');
    $('.tick_' + classes).prop ('checked', false);
}
else
{
    console.log ('unticking');
    $('.tick_' + classes).prop ('checked', true);
}
Carlos.p
  • 11
  • 2
-1

If your form is created dynamically then it might behave differently than static. Use bind or live instead of click for dynamically created elements:

 $(".lessoncount" ).live( "click", function() {
  tickAlike();
});
maque
  • 676
  • 1
  • 6
  • 11