Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery: Problems with detecting if a checkbox is checked

I'm creating a small jQuery plugin for my CMS that styles certain form input types (just radio, checkbox at the moment). It works by hiding the original form element and placing a normal HTML element (for styling with CSS) in the input's place. It then detects actions on the element and updates the original input accordingly. Additionally, it will also work when the associated label is clicked. Here is my code:

jQuery.fn.ioForm = function() {
    return this.each(function(){
        //For each input element within the selector.
        $('input', this).each(function() {
            var type = $(this).attr('type');

            //BOF: Radios and checkboxes.
            if (type == 'radio' || type == 'checkbox') {
                var id = $(this).attr('id');
                checked = '';

                if ($(this).attr('checked')) {
                    checked = 'checked';
                }

                //Add the pretty element and hide the original.
                $(this).before('<span id="pretty_'+ id +'" class="'+ type +' '+ checked +'"></span>');
                $(this).css({ display: 'none' });

                //Click event for the pretty input and associated label.
                $('#pretty_'+ id +', label[for='+ id +']').click(function() {
                    if (type == 'radio') {
                        //Radio must uncheck all related radio inputs.
                        $(this).siblings('span.radio.checked').removeClass('checked');
                        $(this).siblings('input:radio:checked').removeAttr('checked');

                        //And then check itself.
                        $('#pretty_'+ id).addClass('checked');
                        $('#'+ id).attr('checked', 'checked');
                    } else if (type == 'checkbox') {
                        if ($('#'+ id).attr('checked')) {
                            //Checkbox must uncheck itself if it is checked.
                            $('#pretty_'+ id).removeClass('checked');
                            $('#'+ id).removeAttr('checked');
                        } else {
                            //Checkbox must check itself if it is unchecked.
                            $('#pretty_'+ id).addClass('checked');
                            $('#'+ id).attr('checked', 'checked');
                        }


                    }
                });
            } //EOF: Radios and checkboxes.


        });
    });
};

This works great for the radio, but the checkbox seems to get stuck when clicking the checkbox label for a second time - the first click of the label successfully changes it to the appropriate state, but clicking the label again doesn't change it (however the checkbox itself still works fine). It works perfectly in IE8.

I've checked the id is correct and it is. I've also tried a few other methods I stumbled across of checking if the checkbox is checked, but they either gave the same result or failed altogether. :(

Any help would be much appreciated. Thanks :)

Update Here is the HTML, which is generated by a PHP class. This is after the jQuery has run and added in the span elements:

<div>
    <p class="label">Test:</p>
    <span id="pretty_test_published_field" class="checkbox"></span>
    <input class="checkbox" id="test_published_field" name="test" type="checkbox" value="published">
    <label for="test_published_field" class="radio">Published</label>
    <span id="pretty_test_draft_field" class="checkbox"></span>
    <input checked="checked" class="checkbox" id="test_draft_field" name="test" type="checkbox" value="draft">
    <label for="test_draft_field" class="radio">Draft</label>
</div>
like image 369
Fourjays Avatar asked Jan 21 '23 20:01

Fourjays


1 Answers

Just use the infallible and extremely simple DOM checked property. jQuery is inappropriate for this and apparently makes one of the simplest JavaScript tasks there is error-prone and confusing. This also goes for the id and type properties.

$('input', this).each(function() {
    var type = this.type;
    if (type == 'radio' || type == 'checkbox') {
        var id = this.id;
        var checked = this.checked ? 'checked' : '';

        // Etc.
    }
});

UPDATE

The default action of clicking a <label> element associated with a checkbox is to toggle the checkbox's checkedness. I haven't tried this myself with labels for hidden form elements, but perhaps the toggling is still happening because you're not preventing the browser's default click action. Try the following:

//Click event for the pretty input and associated label.
$('#pretty_'+ id +', label[for='+ id +']').click(function(evt) {
    if (type == 'radio') {
        //Radio must uncheck all related radio inputs.
        $(this).siblings('span.radio.checked').removeClass('checked');
        $(this).siblings('input:radio:checked').each(function() {
            this.checked = false;
        });

        //And then check itself.
        $('#pretty_'+ id).addClass('checked');
        $('#'+ id)[0].checked = true;

        evt.preventDefault();
    } else if (type == 'checkbox') {
        if ($('#'+ id).attr('checked')) {
            //Checkbox must uncheck itself if it is checked.
            $('#pretty_'+ id).removeClass('checked');
            $('#'+ id)[0].checked = false;
        } else {
            //Checkbox must check itself if it is unchecked.
            $('#pretty_'+ id).addClass('checked');
            $('#'+ id)[0].checked = true;
        }

        evt.preventDefault();
    }
});
like image 200
Tim Down Avatar answered Jan 31 '23 19:01

Tim Down