Reputation: 537
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>
Upvotes: 4
Views: 6356
Reputation: 832
The checked
attribute of a checkbox is mapped to the defaultChecked
property and not to the checked
property. Use the prop()
method instead.
If elem
is the checkbox, use:
elem.checked
or
$(elem).prop("checked")
This gives us the proper state information of the checkbox and changes as the state changes.
This seems to be the reason of confusion in many cases. So its good to keep in mind the underlying reason behind this.
Upvotes: 1
Reputation: 324567
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();
}
});
Upvotes: 5
Reputation: 1104
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" />
Upvotes: 0
Reputation: 22485
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
Upvotes: 3