I needed some tooltips for a thing. In looking around for a little inspiration, I didn’t have to go further than my dock:

This is where I ended up:

The HTML: Keepin’ it Clean
Links can have tooltips in HTML with no fancy coding whatsoever. Just give a link a title
attribute.
<a href="#" title="Hi, I'm a tooltip thingy.">link</a>
Then when you hover over that link a second or two, you’ll get the yellow box thingy:

Let’s not mess with that. Our HTML will be exactly what is shown above.
The thing is, that yellow box thingy is absolutely unstyleable. Maybe someday it will be once shadow DOM stuff like that is specced out, but today, it’s not possible in any browser. So we’re gonna run some JavaScript which is going to yank out that attribute and make <div>
‘s with the same text, which we’ll position/hide/show as needed.
The JavaScript: jQuery Plugin
As usual around these parts, we’ll use jQuery. This is the perfect kind of thing to make into a plugin. You might want to use this functionality on an arbitrary collection of elements of your own choosing, as well as chain it, so the plugin pattern is ideal.
Calling it will be as simple as:
$("article a[title]").tooltips();
That will tooltip-ize all links with titles that happen to be inside <article>
elements. You could make that selector whatever you want.
Our plugin will do this:
- Loop through each link
- Create a (hidden) div.tooltip for each of them, the text inside matching that links title attribute.
- Remove the links title attribute (only way to get rid of default yellow box popup
- When link is hovered on…
- Position the tooltip accordingly and slide it into visibility.
- When link is hovered off…
- Slide link out of visibility
Massive code dump, commented for your pleasure:
// IIFE to ensure safe use of $
(function( $ ) {
// Create plugin
$.fn.tooltips = function(el) {
var $tooltip,
$body = $('body'),
$el;
// Ensure chaining works
return this.each(function(i, el) {
$el = $(el).attr("data-tooltip", i);
// Make DIV and append to page
var $tooltip = $('<div class="tooltip" data-tooltip="' + i + '">' + $el.attr('title') + '<div class="arrow"></div></div>').appendTo("body");
// Position right away, so first appearance is smooth
var linkPosition = $el.position();
$tooltip.css({
top: linkPosition.top - $tooltip.outerHeight() - 13,
left: linkPosition.left - ($tooltip.width()/2)
});
$el
// Get rid of yellow box popup
.removeAttr("title")
// Mouseenter
.hover(function() {
$el = $(this);
$tooltip = $('div[data-tooltip=' + $el.data('tooltip') + ']');
// Reposition tooltip, in case of page movement e.g. screen resize
var linkPosition = $el.position();
$tooltip.css({
top: linkPosition.top - $tooltip.outerHeight() - 13,
left: linkPosition.left - ($tooltip.width()/2)
});
// Adding class handles animation through CSS
$tooltip.addClass("active");
// Mouseleave
}, function() {
$el = $(this);
// Temporary class for same-direction fadeout
$tooltip = $('div[data-tooltip=' + $el.data('tooltip') + ']').addClass("out");
// Remove all classes
setTimeout(function() {
$tooltip.removeClass("active").removeClass("out");
}, 300);
});
});
}
})(jQuery);
Couple things to note: 1) The final demo is going to have the tooltips animated a bit. None of that happens here in the JavaScript. Animations are design. Design is CSS. Thus, we do all that in the CSS. 2) One little design touch on these is that the tooltips slide in and slide out the same direction. In CSS, by just adding and removing a single class and using transitions, that’s not really possible. The transition will run in reverse upon the classes removal and thus will slide out same direction it came in. By using a setTimeout
, we can apply a temporary class, and animate that as well. Any more elegant ideas for that, let me know in the comments below.
A Semantic Bummer?
Adding divs to the bottom of the document feels like a bummer; just doesn’t feel very semantic. They aren’t connected to the links they came from in any meaningful way through HTML alone.
Also removing the title tag doesn’t sit quite right with me. I’ve heard they don’t do much for accessibility anyway, but still. I wish preventDefault()
would prevent that from showing, but it does not.
Any ideas on that stuff, let’s hear ’em in the comments below.
The CSS: Close Counts
Notice in the OS X screenshot at the top of this article, the background and borders themselves are a bit transparent. I was unable to achieve that here. Those things are possible in general, but the pointy arrow down is an additional element and how the borders and backgrounds connect to each other is just through overlapping and it looks bad with any transparency at all. So, solid colors, NBD.
Oftentimes pointy arrows like we have here are doable with no additional markup or images by using pseudo elements and CSS triangles. In our case, we are going to use a pseudo element, but we’ll need a real element as well. The real element (the <span> that the JavaScript inserted in each tooltip <div>
) serves as a positional box and does the cropping. The pseudo element is the actual pointer. A box styled the exact same way as the tooltip, only rotated 45deg and cropped out by it’s parent. Here’s the schematics:

And so:
.tooltip, .arrow:after {
background: black;
border: 2px solid white;
}
.tooltip {
pointer-events: none;
opacity: 0;
display: inline-block;
position: absolute;
padding: 10px 20px;
color: white;
border-radius: 20px;
margin-top: 20px;
text-align: center;
font: bold 14px "Helvetica Neue", Sans-Serif;
font-stretch: condensed;
text-decoration: none;
text-transform: uppercase;
box-shadow: 0 0 7px black;
}
.arrow {
width: 70px;
height: 16px;
overflow: hidden;
position: absolute;
left: 50%;
margin-left: -35px;
bottom: -16px;
}
.arrow:after {
content: "";
position: absolute;
left: 20px;
top: -20px;
width: 25px;
height: 25px;
box-shadow: 6px 5px 9px -9px black,
5px 6px 9px -9px black;
transform: rotate(45deg);
}
.tooltip.active {
opacity: 1;
margin-top: 5px;
transition: all 0.2s ease;
}
.tooltip.out {
opacity: 0;
margin-top: -20px;
}
Note the .active
class is what does the slide in animation and the .out
class does the slide out.
Thanks
To Adrian Adkison for helping with some ideas and code along the way.
Fair Warning about Opera
Opera doesn’t support pointer-events
in CSS, so this whole demo will appear not to work. That’s because the tooltips are actually positioned right on top of the links with just their opacity turned down to 0. If you want to fix it up in Opera, you’ll need to literally display none/block them in the JavaScript, or use CSS to move them off the links far enough they don’t impede clickability. If someone wants to make that change but have it otherwise function exactly as this demo is, I’m down to update it.
Demo
See the Pen
Bubble Point Tooltips by Chris Coyier (@chriscoyier)
on CodePen.
MMM looks interesting! :)
Interesting.
Anyway I would like to point out that it doesn’t work on IE8: the tooltips appear all together. In the first tooltip there’s the after the heart – don’t know if it’s meant or not ;)
To deal with IE 8, you could:
not sure it’s better, as it’s quite a bit more verbose, but instead of
setTimeout
you might be able to bindanimationEnd
and in the callback add/remove a class.i was able to apply similar logic and apply a new
animationName
to trigger a new animation out, but haven’t tried it with a non-keyframe animation.and then you have to deal with the various browser prefixes for
animationEnd
, but it’s just a couple lines.Looks and works awesome, I can see this coming in useful, thanks!
Also, your comment form is awesome, I’ve been clicking in and out of the text area for about five minutes now.
One thing I’m worried about is how the accessibility of the links is affected when you remove the title attribute. Since that information is meant to provide extra guidance and meaning to the link, removing it to another element seems like it might make the piece less accessible to some users.
(Though if users with devices for accessibility don’t have JavaScript, they won’t lose anything.)
Here’s some pretty old research. http://www.paciellogroup.com/resources/articles/WE05/forms.html
At that time, seems like for LINKS, the majority of readers did not support (i.e. do anything with) the title attribute. And the ones that did, didn’t do it by default.
it doesn’t work on FF 3.6.24 (= latest),
under XP-Pro SP3 (fully updated).
No pop ups appear at all,
when hovering or even clicking
on the Demo links!.. :-(
you should update your browser… I think the latest FF is 8.0
tooltip.js is never loaded in any browser except IE > 8?
Yeah that’s not good. See above comments for correct code.
Looks like you’re having some compatibility issues. Well, when you address them, I’ve got a suggestion for the removing the title attribute.
Instead of removing it when you set up your divs, why not remove it when the link is moused over, then add it back on mouse leave. That way you could still keyboard focus and see the title (for accessibility), but when using the mouse you’d get the visual upgrade.
One thing I definitely don’t like about it is that I can’t hover over the tooltip itself once it appears. I’m out of luck if I want to copy the tooltip text.
Using hoverIntent can fix that.
Okay it works now on Firefox. Guys, do a hard refresh on the page as it might be cached. COMMAND+SHIFT+R on Firefox.
A very easy way to do that is using the Twitter Bootstrap CSS library. http://twitter.github.com/bootstrap/
I’m surprised you did this with JavaScript instead of pseudo elements , pseudo classes, and content:
Pseudo elements are involved. But, yeah, there are ways to do styled tooltips with ONLY CSS and no JavaScript. But it’s far more limited. For example, I wanted to use the title attribute as tooltips naturally have. Can’t do that without some JavaScript, as there is no way to prevent the default yellow popup. You also can’t get the exact shape with arrow and borders with only pseudo elements. You also can’t get the same-direction movement. So yeah, I went with JS to get all that.
one word “WOW”
Problem I’ve been having is that it breaks pretty substantially on mobile. It requires a click to open which is understandable, but there is no way to close it. This is a big problem because it covers everything underneath and you can’t get to it.
Is there a way to remove a hover state on the second click? I haven’t figured out a way yet.
Great code, but it jQuery and problems with my browser Opera ;/. Nevermind, hope that will be soon possible on html 5 :)
Great write up. One alternative for the tip pointer to make it compatible in browsers that don’t support rotation: use a CSS “border triangle” using something like this (not tested):
.arrow {
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid #fff;
}
.arrow:after {
width: 0;
height: 0;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid #000;
content: '';
position:absolute;
top: -2px;
}
Although probably not as crisp as a rotated div, it would work in IE7+.
Like at http://tutorialzine.com/2010/07/colortips-jquery-tooltip-plugin/
Moreover, you can use
.tooltip:before
and.tooltip:after
instead of.arrow
and.arrow:after
, thus no need in extra element.CSS triangles are rad. The border idea there too is a good idea. You’d probably have to increase the size of the triangle all together to get the cleanest border. But yeah crispness tends to be an issue. It’s best used when the contrast between the triangle and it’s background isn’t very high. At worst (black on white), there is a lot of “stairstepping”
@GreLI except not, because in my design one needs to be the child of the other. (See schematics above).
i think it would be more semantically-friendly if tool-tips were added only when needed ( on hover )
quick example:
http://jsfiddle.net/stryju/Ygn42/
this way You don’t pollute the document with random tool-tip elements AND can remove the title ONLY when needed ( on tool-tip appearance )
@stryju: Your code is very very nice and neat — this is definitely an opportunity to use the .data() function in jQuery. Well done.
thanks!
updated it a bit, so it has proper position and “crispy arrow” ;-)
I like that idea. I think there might be advantages doing it either way. With my way all the dom manipulation happens up front, so possibly slower execution time right away, whereas with yours there is dom manipulation with every hover, so slower throughout usage of the page.
minimal DOM manipulation – only the handlers are attached, no DOM manipulation done = better performance imo
i think the big + of this approach is: no dirty DOM ( why to keep tool-tips in DOM if they can never be used? ).
If u want to pollute the DOM, why to create X number of tool-tips, not one and then give it the text ( repaint will happen anyways )?
1 tool-tip, text changed on hover = cleaner DOM
correct me if i’m wrong here ;-)
“Animations are design. Design is CSS.”
I’m not agreeing. There is a certain appetite for the web devs to mess things up and muddy the waters on the normal meaning of words, that’s going on for years now.
This one is no different. Design is design. Animation is action, an action that you have to design *too*.
And I’m pretty sure everyone that knows or have heard just vaguely about cartoons or animations, will tell you the same thing: animation is not design, is something that design is applied to. Web dev is no different.
I thinks the “<” at the beginning of the selector does not need to be there:
$tooltip = $(‘<div[data-tooltip=’ + $el.data(‘tooltip’) + ‘]’).addClass(“out”);
Cool fixed that. Amazing that it even worked at all with that typo.
Really nice, but if it don’t work on old browsers, limit its use. Always is the same problem ;(
Quote:
To avoid unintended breakage and/or unescapedness security holes, use .text() instead of implying .html()-like behavior (which is currently due to injecting it raw in between the opening/closing tags). Instead:
Key being the use of .text(), the rest is just general good practice or preference.
wouldn’t that break the latter
$tooltip = $('div[data-tooltip=' + $el.data('tooltip') + ']').addClass("out");
?i think it does break it – jquery pulls data-* attribute to arbitrary .data() of object, not the other way around
short test: http://jsfiddle.net/stryju/QjMYC/
i meant the
.data('tooltip', i)
line ;-)Good one. I recently build created a jQuery based tooltip similar way. The one I build had to show html content. Also at any point there will be only one in the dom. I replaced the content of this div with the new content.
It would be nice if user can click a menu item or link in the popover
You could alter this to do it’s actions on click rather than hover. Just wasn’t the goal here.
Another point to be made though is that the tooltip div is not a descendant of the anchor link. If that was the case, by default we would get better behavior. The tooltip wouldn’t go away if you moused over it, allowing you to do things like copy the text of it. But unfortunately even though you can put divs instead a’s in HTML5, the behavior doesn’t work quite right in this case.
Great tutorial its helped me a lot i used this tool tip in many web sites.
Works fine now.
Eyeballing the code, it looks like there’ll be some conflicts if you call tooltips() twice using different selectors. i.e multiple tooltips will appear on hover as there’ll be multiple divs with the same data-tooltip value.
Pretty cool, but like some others have already mentioned, you could do this all with CSS. I actually had a challenge like this in one of my recent projects. Check out this fiddle: http://jsfiddle.net/r7Q9m/
Works in IE8+, doesn’t rely on the
-webkit-transform
for the triangle (though you don’t get the border around the tooltip) but still – it works, and well.I get that the jQuery means we can have animation and drop the default behavior of the
title
attribute, but is that really a great thing? Sure the yellow text might be annoying, but with the CSS-only technique it’s just a dash ofpreventDefault
and hey presto – simple, CSS-only tooltips that work in IE8+, and the default title behavior for older browsers.Wonderful!!!
Very clean and simple, I like it.
Just to be clear, you said ” you could do this all with CSS” referring to my example, which is not true.
– You can’t get the same-direction sliding
– You can’t remove the yellow popup (even your example has that, preventDefault does not work)
– You can’t get shadowed/bordered triangles
So yeah, awesome demo, love it, just don’t want to be clear that I wasn’t using JavaScript extraneously, I had some things I wanted to do that it’s the only way.
Nice plugin.
Should you be using
offset
overposition
for relatively positioned elements?I think you might be right. From the docs:
Although in my simple demo case it seems to work OK. For distribution as a plugin though, I’ll have to test it out and probably replace it.
Chris, this line sticks out for me.
“Animations are design. Design is CSS.”
I know this is slightly irrelevant, but I have always held this same opinion, much to the usual disagreement of others. So it’s nice to know somebody with the standing and reputation you have shares the same view.
nice, looks like iPhone’s tooltip
however, can it be placed by single PNG image with rectangular and downarrow shape rather than creating the tooltip CSS
(considering that browser can display interlaced image from PNG)
If you want to use a PNG instead, the bet way would probably be to use
border-image
, that way it could still be expandable both vertically and horizontally rather than be of a fixed size.See the search button example here: http://ejohn.org/blog/border-image-in-firefox/
I had to add a z-index to tooltip to ensure it was always on top.
Oh, great!!! :(o)
Thank you . Great Jop :)
Interesting how my comment does not show up. It seems that you have too many valid comments going to your spam folder.Anyway:
I noticed that the tooltip did not work for IE8 (which I saw your fix in the comments).
Also for IE9 I noticed that the tooltip is not consistent, meaning that the tooltip shows in one link but not others. Have you noticed this behavior.
For Safari, Google Chrome and Mozilla (all latest versions), the tooltip works great.
Thanks.
Great, I want to use this for my website.
This is not work in IE.
Take a look at the following Fiddle:
http://jsfiddle.net/r7Q9m/
You can create the tooltip with relying on CSS3 only. It mainly depends on if you want to rely on CSS3 or on JavaScript. Probably with the majority relying on JavaScript would result in a higher degree of support.
Using the Pseudo-Elements from “:before” and “:after” for once the content and second the “pin” is enough, too.
Awesome Post! I’m implementing on my own site now!
Not work in Internet Explorer.
It doesn’t work at all in IE 8 and down, so use conditional comments to prevent the scripts from being loaded there (see early comment).
Also read the section about Opera in the article. The problem is pointer-events. You can alter this yourself to move the “hidden” tooltip from sitting on top of the link to solve that problem. For instance, have it be above the link instead and float down and away instead of up and away.
Awesome and very useful!
One question: Is it possible to prevent the tooltip from hovering out of the browser window.
THX!
I was having trouble with the tooltips positioning relative to the wrap container. Pulled my hair out trying to figure it out, played with ‘relative’ and ‘absolute’ all over the place. I finally resorted to changing the
var linkPosition = $el.position();
tovar linkPosition = $el.offset();
even though I don’t think this is recommended.Also, can anyone tell me why this doesn’t work in IE8? I added all the IE specific ‘opacity’ rules, but still doesn’t work at all. It doesn’t look like anything that unusual going on with the JS or CSS, so it can’t be that difficult to at least make a crippled version work in IE8.
Using a span inside of the a element, and positioning it as absolute and show it when hovered would be semantic right?
Looks good, but is a bit problematic. The contents of the title attribute is normally plaintext, but this solution turns it into HTML – but not for everyone, since the plaintext title is used as a failover. So, if you want to have an ampersand in the title text, or maybe even a <tag>, what would you write to the title attribute?
Thus, this tooltip implementation is also a possible attack vector for XSS exploits. Imagine an application that lets users input some text for tooltips, say, image captions. Then the admin wants to fancify the interface with these tooltips. Whoops! Now the users can enter arbitrary HTML code.
My proposition: let the title attribute be plain text. Add another attribute, say data-fancy-html-title when HTML is needed.
Or supply callback for the tooltip contents. Let user decide what goes in the tooltip – if rich text or plain text… I’ve done it with my Elixon Tooltip and it is as universal as it can be… I can use it for so many things now…
It’s a lot of CSS for a little tooltip. I’ve made tooltips in the past, but I always end up making them redundant, by rewriting my content, and just explaining things better. which is sad, because I really like tooltips. :P
Daniel Piechnick
That one doesn’t reposition itself if theres no space in the screen… I’m trying to make one but I think this one is the most efficient and simple:
https://code.google.com/p/littletip/
Thanks for the post!
That would be a nice feature. Not too hard actually, if you use jQuery UI for .postion() instead of native jQuery. .postion() has collision detection and will move what what you are positioning to be on screen.
How to make this tooltip at the bottom? any one help plz
how to make popup stay before mouse out on it ?
TypeError: $(…).tooltips is not a function