Why does toggling a checkbox/radiobutton with JavaScript fail after calling event.preventDefault()?
Given your HTML:
<span>
<input type="checkbox">
</span>
and your code:
$('span').click(function(e) {
e.preventDefault();
$(':checkbox')[0].checked = true;
});
Event bubbling is your issue here.
When clicking the checkbox
, the click event is propagated to the span
. e.preventDefault()
is the called on the event object propagated from the checkbox
.
Hence preventDefault()
is execute against the checkbox
itself, preventing it from being checked.
Ones the click event of the checkbox
is "cancelled" by means of preventDefault
it will stay cancelled until completion of the event flow. (See bottom of answer for more details on that)
If you apply some styling to the span you notice that clicking around the checkbox
your code works as expected but clicking on the checkbox
itself replicates your issue due to the above mentioned.
DEMO - Original Fiddle + styles. Clicking span
is fine, checkbox
is not
According to the MDN documentation on preventDefault():
Calling
preventDefault
during any stage of event flow cancels the event, meaning that any default action normally taken by the implementation as a result of the event will not occur.
The DOM Level 2 Spec also notes:
If an event is cancelable, the
preventDefault
method is used to signify that the event is to be canceled, meaning any default action normally taken by the implementation as a result of the event will not occur. If, during any stage of event flow, the preventDefault method is called the event is canceled.
The DOM Level 3 Events Spec notes under Example 5:
The default action associated with the
click
event on<input type="checkbox">
elements toggles thechecked
IDL attribute value of that element. If theclick
event's default action is cancelled, then the value is restored to its former state.
Wrapping part of the code in a setTimeout
does the trick, as you can see here http://jsfiddle.net/y7C77/
$('span').click(function(e) {
e.preventDefault();
setTimeout(function () {
$('input').prop('checked', true);
}, 1);
});
preventDefault()
cancels the default action, and I think that it's able to cancel it even if you manually do what the browser would do (in this case: altering the checked
property).
This is the expected behavior what I feel since, when you say event.preventDefault() you are instructing the browser not to do any operation on it in this event. So, even after the statement when you explicitly check the checkbox, browser will not do any operation on it. We can see the difference when I change the event as follows:
$('span').click(function(e) {
e.preventDefault();
//$(':checkbox')[0].checked = true;
setTimeout(function(){$(':checkbox')[0].checked = true;});
});
Using timeout we are changing the checked property outside the event, hence its get checked.