Thursday, June 20, 2013

jQuery UI: Icon Widget

The jQuery UI theme framework comes with a bunch of icons. Useful icons. I use them a lot, but not necessarily inside a widget, like an icon button for example. Icons are effective conveyors of information and state. I like user interfaces that user icons to represent things where it makes sense, and to have those icons change state when attention is needed. And adding text to those icons in the form of a tooltip, to further communicate to the user. All great things, all very awkward to implement. I'm constantly repeating myself when using stand-alone jQuery UI icons. So, I went all out. I took care the issue by creating a simple icon widget that took care of the boiler-plate for me.


This is what the markup used to create these three icons looks like.

<span data-icon="folder-collapsed" data-state="highlight">3 new files transferred</span>
<span data-icon="signal-diag" data-state="error">signal strength bad</span>
<span data-icon="power"></span>

And here is the widget itself.

(function( $, undefined ) {

// A widget with the for the sole purppose of displaying
// icons.  We can also attach a tooltip, and change the
// icon state.
$.widget( "ab.icon", {

    _create: function() {

        // We're creating the icon now.  We need the
        // element, the wrapper, and some information
        // about the icon.  The text is that ends up in the
        // tooltip is the element text while the icon and
        // the state are data attributes.
        var $element = this.element,
            $wrapper = $( "<span/>" ),
            text     = $element.text(),
            icon     = $element.data( "icon" ),
            state    = $element.data( "state" );

        // Keep them here so we can restore them later.
        this.originalClass = $element.attr( "class" );

        // Make a tooltip if there is any text.
        if ( text ) {
            $wrapper.tooltip({
                content: text,
                items: "span" 
            });
        }

        // Apply the icon classes.
        if ( icon ) {
            $element.addClass( "ui-icon ui-icon-" + icon );          
        }

        // Apply the state classes.
        if ( state ) {
            $wrapper.addClass( "ui-state-" + state );
        }

        // Do some CSS magic, and wrap out icon in it.
        $wrapper.css( "background-image", "none" );
        $wrapper.css( "background-color", "transparent" );
        $wrapper.css( "margin", "2px" );
        $wrapper.css( "border", "none" );    

        $element.wrap( $wrapper );

        this._super();

    },

    _destroy: function() {

        var $element      = this.element,
            originalClass = this.originalClass;

        // Taking the wrapper off the element is an easy
        // way to clean up.  We're also taking off any
        // classes, and removing and data.
        $element.unwrap()
                .removeClass()
                .removeData( "icon state" );

        // Restore the original classes, if they exist.
        if ( originalClass ) {
            $element.addClass( originalClass );
        }

    },


});

})( jQuery );

$(function() {

    // Nice and easy.  All the icon data is attached to
    // element already.
    $( "span" ).icon();

});