Custom checkboxes and radio buttons with jQuery

A recent project required checkboxes and radio buttons with a custom appearance, and searching around turned up numerous solutions to this need. There were a variety of approaches to solve the problem to varying degrees of completeness. The one I liked best was provided by Filament Group, the talented folks behind jQuery’s UI ThemeRoller. It was relatively lightweight, worked like a charm, and accessible to boot.

However, it didn’t support disabling the controls, something I needed. The developers say they will add this ability, but I needed it quickly, so I figured I’d just add it myself.

I encountered a couple other issues that required tweaks as well, which I’ll describe here for posterity. The first was integrating the jQuery UI dialog widget. I had a dialog with some radio buttons, and they looked great out of the box. Hovers and checked states worked fine, but clicking had no effect. It turns out that the modal div’s z-index was interfering. An odd problem, but easily fixed with some CSS to set the z-index higher than the theme’s modal z-index:
.ui-dialog .custom-radio label {
    z-index: 1006;
}

The second issue was that my application generates markup fragments and binds their event handlers before inserting into the DOM. The original code grabs references to each input’s label and other radio buttons in a radio button set at the time the checkbox or radio is prepared. The problem is, if the markup fragment is not part of the document yet, those references are empty. For the radios, I modified the code so the other radios were not predetermined, but instead are located at the time they’re needed in the updateState() function:
allInputs.each(function(){
  $('label[for='+$(this).attr('id')+']').removeClass('checked');
});

$('input[name='+input.attr('name')+']').each(function(){
  $('label[for='+$(this).attr('id')+']').removeClass('checked');
});

Late-binding like this has the added benefit that if other radio buttons are added to the set after the page loads, they are still accounted for.

The reference to the label could also be solved in the same way, but I solved it by just grabbing the element following the input, which in my case, was guaranteed to be the label. Your mileage may vary.

I decided to make 2 other tweaks as long as I had my mind wrapped around this stuff. First, I eliminated the checkedHover and checkedFocus CSS classes because I don’t think they were necessary (But beware, I have not tested this assumption thoroughly yet). That allows deleting a small amount of JavaScript, but it requires some changes in the original CSS selectors, for example:
.custom-checkbox label.checkedHover
.custom-checkbox label.checked.hover

The final tweak was to replace the inputType variable with a reference to the wrapper div. I had a place where I needed to dynamically change a list of checkboxes to a list of radio buttons (or vice-versa). Having a reference to the wrapper div was much more helpful in this circumstance, and the inputType variable wasn’t doing anything except helping to build the wrapper div anyway, so no sense to keep it in the closure.

You can find the source files here.

4 Responses to “Custom checkboxes and radio buttons with jQuery”

  1. craig says:

    A couple follow-up comments: First, saving a reference to the wrapper div was pointless, because you cannot dynamically change the input’s type from checkbox to radio button (I should have known that), and consequently, the element needs to be replaced and the event handlers hooked up again. I considered using event delegation to avoid having to re-bind events, but in the end decided to just blow away the checkboxes and replace them with a new set of radio buttons.

    Second, this approach works by covering the native checkbox or radio with the corresponding label, which has an opaque background. This means if you need checkboxes to appear on more than one color, you’ll need one image sprite for each background color. I’m okay with this limitation, but it’s unfortunate. I look forward to seeing what jQuery UI comes up with.

  2. craig says:

    To better handle varying backgrounds, I changed the approach from using z-index to position the label over the checkbox element and now just position the checkbox element way offscreen to the left. (And obviously, switched the sprites to an image with transparency.) We’ll see how well it works out; seems good so far.

  3. jfffrsn says:

    thanks i was looking for a way to have the disabled states and this really helped.

  4. Guest says:

    Thanks a lot for blogging about this. I had an issue with label z-indexes, and thanks to you now I have a clean solution.

Leave a Reply