Tuesday, November 19, 2013

jQuery UI: Checkbox Widget

The jQuery UI button widget operates on links and buttons for the most part. It can also operate on checkbox input elements. What's different about these button instances is that they toggle their state, and under the hood, toggle the state of the checkbox input element. This is ideal in that you get your themed button, and your code can still interact directly with the checkbox. However, the issue I've run into with the default toggle button is that it doesn't look like a checkbox. It would be nice if we could still use a themed widget that interacts with the underlying checkbox element, and yet has a more traditional checkbox look and feel.

Let's create a checkbox widget that does just that. Here is the functional example of the widget, that takes surprisingly little code to implement. The end result should looks something like this.

Let's take a walk through how this widget is defined and used. The widget itself is defined in the app namespace, so as not to interfere with the default ui namespace, and it's called checkbox. It inherits directly from the base Widget class. Most of this widget code can be found in the _create() method. So we'll start there.

this.element.addClass( "ui-helper-hidden-accessible" );
this.button = $( "<button/>" ).insertAfter( this.element );

First, we're adding the ui-helper-hidden-accessible class to this.element — the checkbox input element — in order to hide it. Next, we insert our button element next to the checkbox.

this.button.addClass( "ui-checkbox" )
           .text( "checkbox" )
           .button({
               text: false,
               icons: { 
                   primary: "ui-icon-blank"
               },
               create: function( e, ui ) {
                   $( this ).removeAttr( "title" ); 
               }
           });

This piece of code instantiates the button widget. This is the part the user sees, so we have to configure it so that it looks like a checkbox. First, we add our ui-checkbox class to the button, used for applying styles. Next, we give the button some placeholder text. Button widgets need text to render properly, even if no text is to be displayed, as is the case here. Next we call the button widget constructor here we tell the button we don't want an text displayed. We've set the icon to ui-icon-blank — this makes the button look like an unchecked checkbox. Finally, the create callback function removes the title attribute from the button since it only has our placeholder text, and the user has no interest in seeing that.

this._on( this.button, {
    click: function( e ) {
        this.element.prop( 
            "checked",
            !this.element.is( ":checked" )
        );
        this.refresh();
    }
});
            
this.refresh();

Finally, at the bottom of the _create() method, we bind our click event handler to the button. This simply toggles the state of the underlying checkbox element then calls the refresh() method. Let's have a look at that next.

refresh: function() {
    this.button.button( "option", "icons", {
        primary: this.element.is( ":checked" ) ?
        "ui-icon-check" : "ui-icon-blank"
    });
}

The refresh() method is responsible for updating the visual aspect of the widget — the part the user sees. The button can be in one of two states — checked and unchecked — just like a real checkbox element. So all this method does is update the button icon to reflect the state of the underlying checkbox element.

What's really useful about using a button widget for the display of our custom checkbox is that all the focus, hover, and key handling is taken care of for us.