Thursday, February 20, 2014

Using jQuery UI Tooltips With Autocomplete

The jQuery UI autocomplete widget can transform the way each individual item is rendered. By default, it only renders the data label. But that's easy to change by extending the autocomplete widget, and overriding the _renderItem() method. For example, if each object in the autocomplete source has some specific field you would like rendered in the autocomplete drop-down, that's not difficult to implement. The challenge lies with finding enough space on the page to render this additional information. The auto complete already utilizes the menu widget to render the drop-down component — so why not utilize the tooltip widget as well?

The tooltip can come in handy with the autocomplete widget, as seen here, when you're looking to conserve on space, yet you would like to show your users additional data. For example, there's a description associated with each item in the autocomplete source, but you want to keep the size of the drop-down menu under control. And besides, the the user isn't going to be reading each description all at once. It's better, in this scenario, to let the autocomplete render using it's default mode — showing only the label — and provide the description in a tooltip. That way, when the user shows interest in a particular item, they get the additional information they need to make a selection.

You can see that while the user has moved their cursor over "Book 2", they get the description related to that book. So, they get the information they need, and we don't have to bloat the drop-down with unnecessary information. Now let's take a look at how it's done. The strategy is the same as changing how each item is rendered within the drop-down menu — we extend the autocomplete widget. Here's what that extension looks like.

$.widget( "app.autocomplete", $.ui.autocomplete, {
        
    _create: function() {
            
        this._super();
            
        this.menu.element.tooltip({ 
            items: "li[title]",
            position: $.extend( {}, ttpos, {
                my: "left+12",
                at: "right"
            })
        });
         
    },
        
    _destroy: function() {
        this.menu.element.tooltip( "destroy" );
        this._super();
    },
        
    _renderItem: function( ul, item ) {
        return this._super( ul, item )
            .attr( "title", item.desc );
    }

});

The first method this extension overrides is _create(). We use _super() to call the default autocomplete constructor, which creates the menu, and all the other fantastic things that the autocomplete must do when it's created. With that out of the way, we're free to instantiate our tooltip widget on the menu. To do that, we use this.menu.element. The autocomplete stores a reference to the menu widget, not the menu element. The items option specifies with items will have tooltips and is a selector string. Here, we're saying that any li elements with a title attribute should display tooltips. The position option also needs to be modified. Rather than pass an object literal here, we're extending the default position option value. This is stored in the ttpos variable, and comes from $.ui.tooltip.prototype.options.position.

Now that we have a tooltip widget applied to the menu, we need some tooltip content. That's taken care of in the _renderItem() method. The item argument represents the data object. If it has a desc attribute, that gets applied to the title attribute of the li. The tooltip will display this content. What's nice about this approach is that it doesn't require any explicit options added to the widget. The only thing that's different is the autocomplete source data format, and without the desc attribute, the autocomplete will continue to function as normal — it's a fairly innocent extension.