Tuesday, November 17, 2009

Checkbox: Finding the TRUE value of "checked"

Today I had a familiar scenario that turned not-so-familiar, so I'll post my results here for future reference. The core problem is that the value of this.checked within jQuery's .click(function({})) changes depending on what triggered the click event.

The setup: In a form, I had one checkbox which, when clicked, would set some fields based on the values of other fields (think "Click here if same as contact info"). The request: put in a couple more checkboxes for the same fields (now also think "Click here if same as Director info" and "Click here if same as Housing info"). The problem: 1) I can't simply copy field values. I need to save the old ones in case they uncheck the box. 2) Only one checkbox can be checked at a time, i.e., these really need to work more like radio buttons, but at the time, I didn't want to use radio buttons.

Still, it should be pretty simple. I would use jQuery to set the click event function of each checkbox which would save the old values and insert the new. But before it did that, it would need to uncheck any previously checked boxes. I figured I'd do this last bit by using jQuery's .click() to trigger the click event of any checked boxes. And there I found a surprise.

Example HTML:
<form name="newForm" action="" method="post">
<input name="checkbox1" id="checkbox1" value="1" type="checkbox" />
<label for="checkbox1">Checkbox #1</label>
<input name="checkbox2" id="checkbox2" value="2" type="checkbox" />
<label for="checkbox2">Checkbox #2</label>
</form>


Example Javascript:
$(function() {

$('#checkbox1').click(function(e) {
if(this.checked) {

// uncheck the other box by triggering its click event
if($('#checkbox2').attr('checked'))
$('#checkbox2').click();

// do something important
}
});

$('#checkbox2').click(function(e) {
if(this.checked) {

// uncheck the other box by triggering its click event
if($('#checkbox1').attr('checked'))
$('#checkbox1').click();

// do something important
}

});

});


Theoretically, checking one box should uncheck the other (or leave it unchecked if it already is). However, that doesn't actually happen. The value of this.checked changes depending on how what triggered the event.

If the click event of the checkbox is triggered by a mouse click on that same checkbox, this.checked is the new value. But if the click event of the checkbox is triggered by jQuery's .click(), as in the above example, this.checked is the old, previous value that will be changed in a few milliseconds. Thus, if the user has just checked the box with a real mouse click, this.checked equals true. But if your script just checked the box with .click(), this.checked equals false.

If, for whatever reason, you need to know whether a checkbox is being checked or unchecked, and the click event may have been triggered by jQuery's .click() method, you cannot simply rely on this.checked.

What I did to get around this was to pass as an argument to the click function a reference to the node where the click occurred. If the event was triggered by another event, I toggle the value of checked.
$('#checkbox1').click(function(e,node) {
var checked = this.checked;
if(typeof(node)!='undefined')
if(node.getAttribute('id')!='checkbox1')
checked = !checked;
....
});

$('#checkbox2').click(function(e,node) {
....
$('#checkbox1').trigger('click',this);
});

Labels: ,

Javascript Toggle Boolean

I'll put this here for future reference. Found it on the blog of Jerad Bitner.

var bool = true;
bool = !bool // it's been toggled!

Labels: