Saturday, July 20, 2013

Finding a jQuery Tooltip Plugin that Meets Your Needs

The humble little Tooltip has evolved from simply showing a bit of text over the page using the title or alt attribute of a link or image element to something closer to an Infobox or Popover displaying rich, dynamic content enabling user interaction. There are so many libraries that provide tooltip features from the simple hover over text to these interactive information boxes that finding the one that meets your needs while staying small and maintaining consistency with your site can become a daunting task.

I set out to review a few of the more popular libraries out there to see what features they provided and how well they could adapt to the different uses I had in mind on my site. I limited my comparison to jQuery plugins that were either well followed on GitHub or part of another library. In the end, I settled on the following four evaluating them based on these criteria:

  qTip2 jQuery UI PowerTip Bootstrap
Content ***** *** *** ***
Styling ***** *** ** **
Positioning **** ***** ** *
Triggering ***** * **** **
Animation ***** ***** * *
Overall ***** **** *** **


The ratings simply indicate the depth of options available in each category. This means if you just need a basic tooltip, you can stick with PowerTip or Twitter Bootstrap. If you need more functionality and control, you may want to consider qTip or jQuery UI. I spent some time trying to use the various features available in each category and share those results below. I also copied all the demos to my sandbox for easier review and access to the source code.

Setup

Before doing anything, I had to make sure all the libraries would work together since I wanted to create side-by-side comparisons of each one. In this post, I'm loading all the libraries from a CDN source except Twitter Bootstrap. The CSS libraries do not play well with my blog so I built a custom version that included only the tooltip and related styles:

Both jQuery UI and Bootstrap use "tooltip" for the name of the plugin so I used $.widget.bridge to create a different name for the jQuery UI version and allowed the Bootstrap plugin to stay named "tooltip" (trying to use the noConflict option on the Bootstrap widget just resulted in a lot of errors):

Everything else seemed to work fine so its time to dive in and make some tooltips!

Tooltip Content

The first feature category is how to define and manipulate content for the tooltip. For me, this was one of the more important features. I would like to have the flexibility to use the basic attribute method of defining content or setting it from code either statically or via an AJAX call.

Basic Title Attribute

All the tooltip libraries will automatically use the "title" attribute to define the content of the tooltip. This makes it very easy to initialize all the tooltips on a page at once:





Programmatic Content

Now that the simplest case is handled, let's dig a little deeper. All the libraries provide a means to set the content from code. However, they do differ slightly on how you achieve setting the content. qTip and Bootstrap were the easiest and most intuitive providing content/title options in the plugin function call. jQuery UI has a content option, however, it didn't work unless the title attribute was set on the element. You either have to put an empty attribute there or use $.attr to set the title attribute before calling the plugin. PowerTip has no option to set the content when initializing the plugin. Instead, you use $.data('powertip', ...) on the target element to set it.





Dynamic Content

Dynamically loading content into the tooltip is another challenge. None of the libraries have built-in capabilities to do this so you'll have to know when the tooltip will become visible, run an AJAX call, and then change the content on the tooltip. In my tests, I wanted to ensure the tooltip was shown as if it had content. However, since it was not loaded yet, show a spinner indicating it was being retrieved. Upon receipt, the spinner content would be replaced with the actual content from the AJAX call.

qTip

Overall, I found that only qTip made dynamically changing content straight forward while everything else proved to be a bit more challenging. qTip exposes an api object in the show event which enabled me to simply set the content without any other issues. Internally, it updated the DOM element's content and fixed any positioning/sizing issues.

jQuery UI

There is a callback available to the function that is used on the content option which would allow using an AJAX call in that function. However, it was not possible to show the tooltip first with the spinner and then replace with the AJAX content later because, after its open, the widget will no longer internally run through the logic of setting the content and positioning the tooltip element. As such, a slightly different approach was required to achieve the desired effect. The open event is triggered when the tooltip is displayed and, as is standard with all jQuery UI event, the second parameter in the handler function will be an object with useful information related to the event. In this case, ui.tooltip will contain a reference to the tooltip DOM element. I setup the tooltip to show the spinner by default and in the open event performed an AJAX call to load the dynamic content. Once complete, I directly changed the tooltip element and manually repositioned using the jQuery UI Position plugin (which is used internally by Tooltip to do the positioning anyways).

PowerTip

It wasn't exceptionally difficult to get the dynamic content to load into the PowerTip version. It does trigger several events that you can listen to and then alter the content. I put the spinner into the powerTipPreRender and then the AJAX call in the actual powerTipOpen. I did this since content can be defined here via $.data and the positioning would be calculated. Once in the open event, you have to directly change the #powerTip element. You can do this because PowerTip, by design, will never show more than one tooltip at a time. However, once the content is set, the positioning will not be correct. Luckily, as of version 1.2, there is a reposition method available which will handle recalculating and moving the visible element to the desired location.

Twitter Bootstrap

This plugin was designed to initialize once and be done. You can change the content of the tooltip between show/hide but not via the tooltip() plugin call. Instead, you have to change the data-original-title attribute via $.attr(), then show will update the content. However, there is no easy way to reposition the tooltip once its visible. So the spinner will initially be shown in the correct position, but the updated HTML from the AJAX call will cause the tooltip to shift out of place. What I had to do was put the tooltip into manual mode and listen to the mouse events myself. This means the delay option becomes meaningless, if you need it, you will be required to implement it yourself.



The final outcome of all that work looks like this:

Styling

In theory, you should be able to change the style on any of these tooltips to match your needs. You may have to find the classes each one uses and override them as necessary. But, in the end, its completely feasable. qTip does have some built-in styles you can choose from as well as being able to use any of the jQuery UI themes. If you want to theme tooltips differently on the same page (like I will be doing below), you may have to do different things to enable that in each library. Both qTip and jQuery UI allow adding a custom class to the tooltip element so you can easily target it in the CSS. PowerTip only creates one element in the DOM and uses a predetermined ID for that element. Before showing the tooltip (in the powerTipPreRender event), you could change the class on that element so you can create different styles. The Bootstrap plugin allows you to add the tooltip element to different containers so you can create different containers with classes that allow targeting the tooltip and changing the styles. As an example, I restyled the jQuery UI tooltip to match the Twitter Bootstrap plugin.





Here's what the tooltip looks like after the new styles are applied:

Positioning

qTip and jQuery UI have the most flexible positioning options. jQuery UI uses the Positioning plugin to align the tooltip to the target element using top/middle/bottom and left/center/right with offsets. qTip uses a similar syntax but does not support percentage based offsets like jQuery UI. PowerTip does support eight different positioning points while Bootstrap can only support four. Most likely, the PowerTip options would be sufficient. However, in some situations having the full power available in jQuery UI may be desirable.

Triggering

For a normal tooltip, automatically showing it when you hover over the target and then not showing it when you move off the target is the typical behavior. All the libraries support some kind of hover intent option to delay showing the tooltip such that tooltips don't just flicker as the mouse moves across the screen. If you need more control, qTip and PowerTip seem to have the best options available for both controlling what event(s) trigger the display of the tooltip, whether to allow interaction with the tooltip after its visible, and what event(s) cause it to disappear. jQuery UI and Twitter Bootstrap require some hacking to get a similar result. Below, I've made all the tooltips show on hover but used various options/techniques to keep the tooltip open so the user could interact with the content in the tooltip. For this experiment, I simply added a link in the tooltip which would trigger some other action (in this case, an alert box) when clicked.

I was able to add the HTML directly into the title attribute except jQuery UI which just showed the escaped HTML:



Additionally, I had to listen for click events on the link from the body since the tooltips are not always in the DOM nor do I know exactly where they'll be attached:



qTip

The easiest approach with qTip is to adjust the hide option on the tooltip to use a combination of delayed and fixed. The hide.delayed option will provide time for the user to shift from the target link to the tooltip. The hide.fixed option tells qTip that mousing over the tooltip should keep it open since you intend to allow the user to interact with the tooltip. If the user does not move to the tooltip within the 500ms delay, it will be hidden.



jQuery UI

jQuery UI lacks a manual trigger option which requires you to know how the internal widget code binds to the mouseleave event so you can prevent it from hiding the tooltip. After some studying, I found that shortly after the open event is called, the mouseleave event is bound. Unfortunately, that means we can't turn it off in the open event. However, we can use a setTimeout to run a function immediately after the current call stack finishes executing. Once the event is disabled, the tooltip won't hide until we explicitly tell it to. The easiest way to do this is to listen for click events on the body:



PowerTip

Similar to qTip, PowerTip provides options to delay closing the tooltip and enable hovering over the tooltip to prevent the tooltip from closing:



Twitter Bootstrap

Bootstrap doesn't have the options qTip or PowerTip provide. However, it does have a manual mode such that we can bind to the mouseenter event and control the behavior. The implementation is similar to the jQuery UI version but does not need the hack to unbind the mouseleave event:



I should also point out that Bootstrap does have a Popover widget that, by default, remains open. It includes a title for the content and different styling but may be a better option for this use case.

Here's the above code in action:

Animation

I honestly think a simple fade in/out is probably enough for many applications. However, if you use qTip or jQuery UI, you get all of the effects available in the jQuery UI library. With PowerTip and Twitter Bootstrap, you'll be stuck with fade in/out or nothing.

Final Thoughts

After reviewing the different libraries, it became clear that I needed the most control over the content and triggering features of the tooltip. I could live with limited positioning options and only a fade animation. Styling isn't much of a problem since I know I can always alter the CSS to meet my needs. However, your needs may be different and you should evaluate the libraries based on those requirements. One other consideration to take into account is the size of the libraries (these are all minified, gzip compressed):

  JS CSS
qTip 15K 2.2K
jQuery UI 60K 6K
qTip 3.3K 440B
Twitter Bootstrap 8.9K 26K


Of course, jQuery UI and Twitter Bootstrap have plenty of other goodies available. My custom build of Bootstrap that only contains the tooltip and popover is 3.5K. You could make a custom build of jQuery UI too that only included the tooltip and it would probably be about the same size. Given those numbers, qTip is almost five times larger. So while it does a lot, it comes at the expense of size. If you're already using either jQuery UI or Twitter Bootstrap, unless you have a real need, it might be best to adapt the tooltip to work so you don't need to add another library. However, choice is good and there are a lot of options available. In the end, if you need something simple, any of these libraries will deliver. If you need something special, you'll either have to adapt one of them or track down something that meets your needs.