Wednesday, March 20, 2013

Using jQuery UI Tooltips With Flot

Flot charts work nicely in jQuery projects. Especially since it's targeted for jQuery-based code. These charts can work well in jQuery UI projects too because they're very granular in their configuration values. You can make the charts look like they're seamlessly integrated into your theme. There was, however, one place I was having trouble with until recently — tooltips. I like being able to show the user some more detail when they hover over data points. Flot even provides an example of how to do this using a generic tooltip component, constructed on the fly. I want to show jQuery UI tooltips using the same approach. This is what I came up with.


I'm not going to bother with the HTML because it is just a div element with an id attribute and some styles to set the width and height of the chart. Here is the important stuff - the Javascript that creates the chart and binds the hover event handler to the chart. The event handler is actually what we're really interested in here because this is where the tooltip widget gets created and displayed when the user hovers over the data point.

$(function() {

    var $chart = $( "#chart" ),
        ttPos = $.ui.tooltip.prototype.options.position;

    $.plot(
        $chart, 
        [{
            label: "Number of users",
            data:[
                [ 1, 2233 ],
                [ 2, 1294 ],
                [ 3, 1658 ],
                [ 4, 1603 ],
                [ 5, 1790 ],
                [ 6, 2103 ]
            ]
        }],
        {
            series: {
                lines: { show: true },
                points: { show: true }
            },
            grid: {
                hoverable: true
            },
            legend: {
                show: false
            }
        }
    );

    $chart.bind( "plothover", function( e, pos, item ) {

        var isTooltip = $chart.is( ":ui-tooltip" );
        
        if ( item !== null && isTooltip === false ) {

            var label = item.series.label,
                data = item.datapoint[1],
                content = label + " " + data,
                evtPos;

            evtPos = $.extend( ttPos, {
                of: {
                    pageX: item.pageX,
                    pageY: item.pageY,
                    preventDefault: $.noop
                }
            });

            $chart.attr( "title", content  )
                  .tooltip( { position: evtPos } )
                  .tooltip( "open" );

        }
        else if ( item === null && isTooltip === true ) {

            $chart.tooltip( "destroy" );

        }

    });

});

The flot chart itself is trivial — one data series with minimal data. The callback function we're binding to the plothover event is the part we're interested in here. This is where we create the jQuery UI tooltip instance and display it. The tooltip content is based on the label and the data point value — fairly straightforward. There are a couple tricks involved with making it work.

The tooltip widget itself uses the chart div as it's element. And so each time the hover event is fired, we need to check if the chart is also a tooltip. This is done using the pseudo-selector :ui-tooltip and stored in the isTooltip variable. If there is item data, meaning the user hovered over a data point and not just empty chart space, then we're going to want to display the tooltip — unless we're already displaying it. Likewise, when the item is null, we're going to destroy it — unless that has already happened too.

The other challenge is with positioning the tooltip widget. The evtPos variable is passed to the tooltip constructor, which is in turn passed to the position widget. What we're doing here is extending the default position object the tooltip uses with an of attribute. This is the object we position against, and in this case, we want to position against the mouse event. And so we create our own simple event object, using an interface that the position utility understands. Specifically, the existence of the preventDefault attribute is how the position knows that this is a mouse event and to use the appropriate coordinates.