Saturday, April 20, 2013

Programmatic Get/Set of jQuery UI Buttonsets and Checkbox/Radio Group Values

I much prefer the look of a jQuery UI Buttonset over a list of radio buttons. Mixing Buttonsets with groups of check boxes enables a consistent look with easy control over the styling. However, if you need to manipulate the state of the underlying form controls, it becomes a little less clear how to handle that situation.

I've never been a fan of the inconsistent interface to getting/setting DOM form elements. jQuery only insulates us slightly from the problem. You can safely say $('input').val() and that will return the contents of the input box if its type is "text". A check box or radio button don't work the same way. Calling the val() function on one of these elements won't tell you if its checked. Instead, it will return the value of the value attribute. The honor of determining the state of the check box or radio button is reserved for the prop() function. Using prop('checked') allows us to determine if a checkbox or radio button is checked. Additionally, jQuery sets up the handy :checked selector to find elements that are checked.

So how does this affect a Buttonset tied to a radio group? Knowing the above will allow you to interact with radio buttons programmatically like the Buttonset widget does internally. Since there is no value() function built in to the Buttonset widget, you have to write your own get/set function. After some trial and error, I found the following worked quite well:


<div id="mygroup">
<input type="radio" name="mygroup" id="option1" value="1" /><label for="option1">Option 1</label>
<input type="radio" name="mygroup" id="option2" value="2" /><label for="option2">Option 2</label>
<input type="radio" name="mygroup" id="option3" value="3" /><label for="option3">Option 3</label>
</div>


The Buttonset widget needs a container element for the inputs it will manage. Additionally, each input needs a label element so the buttons render properly. The input attribute name="mygroup" establishes the radio group so the Buttonset will toggle the state correctly. Creating the Buttonset is easily done by selecting the containing div:


$('#mygroup').buttonset();


By itself, you don't need to do anything else to interact with the group. However, if you need to manage it from code, you'll need a get/set function to control it:


function myGroupVal( val ) {

if ( typeof(val) != 'undefined' ) {

$('#mygroup')
.find('input')
.removeProp('checked')
.filter('[value="'+val+'"]')
.prop('checked', true)
.end()
.end()
.buttonset('refresh');

} else {
return $('#mygroup').find(':checked').val();
}
}


When setting the value, the Buttonset requires a refresh so it updates the state of the buttons that represent the underlying radio group. I like to set the value attribute on the individual radio inputs and use that as the actual value of the group.

Here's a jsFiddle demo that uses an input box and some buttons to get/set the Buttonset.

Buttonset can also be used with check boxes such that more than one item can be selected at a time. The above example assumes only one item can be selected and ensures everything else is unchecked before checking the new item based on the passed value. For check boxes, you could work with an array of values to get/set such that more than one item could be checked at a time. Otherwise, as it stands now, the code will simply mimic a radio group even though the inputs are check boxes (try it).