views:

85

answers:

3

Hey, 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>
+2  A: 

hi there,

i've found on the odd accassion that i need to use the alternative of:

$("#checkboxID").is(':checked');

this may or not be an alternative in your scenario, but worth noting. Also, you may have to add the 'live' event, rather than 'click' if changes to the dom are occurring. i.e.

.click(function()

to

.live('click', function()

jim

jim
Unfortunately neither have made any difference. :(
Fourjays
+2  A: 

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();
    }
});
Tim Down
+1 for thinking outside jQuery :)
alex
Just tried this and it has made no difference. The issue seems to be that it is either not updating the checked status or is not detecting it. I've also used live now as suggested by jim but that has also made no difference. If it makes a difference, the checked attribute does NOT appear or disappear in the elements display of Chrome's developer tools. If I show the original checkbox next to my "pretty" one it is still checking/unchecking exactly as the "pretty" one is. Also, this problem only seems to have started since I turned it into a jQuery function.
Fourjays
Amended my answer.
Tim Down
Brilliant! Preventing the default event did the trick. :D Seems so obvious now too. Thanks! :)
Fourjays
A: 

Could you show us the HTML? I am looking to see if you set a value property for the checkbox in the HTML. There is a possibility that broke it.

<input type="checkbox" value="This May Break Checkbox" />
<input type="checkbox" name="This Checkbox Works" />
abelito
Added the HTML to my original question. It did have a value set as it comes from a PHP class. I've now changed the class so it no longer adds a value for checkboxes. No change though. :(However, the checkbox itself (which for testing is displayed alongside) is not changing now when the label is clicked, but the "pretty" one updates once before getting stuck (because the checkbox is not being actually checked/unchecked).So it seems the problem is the actual checking of the checkbox via the label.
Fourjays