In particular, Jorge Mesa writes to ask how to re-create their ‘puff’ popup bubble shown when you mouse over the download image.
In essence the effect is just a simple combination of effect, but there’s a few nuances to be wary of.

How to Solve the Problem
To create the puff popup bubble effect, we need the following:
- Markup that assumes that JavaScript is disabled. It would be fair to say that the popup would be hidden from the CSS.
- The hidden popup, correctly styled for when we make it appear.
- jQuery to animate the puff effect on
mouseoverandmouseout.
The biggest trick to be wary of is: when you move the mouse over the popup, this triggers a mouseout on the image used to trigger the popup being shown. I’ll explain (carefully) how to make sure the effect doesn’t fail in this situation.
I’ve provided a screencast to walk through how create this functionality. Details on how and what I used can be found below.
Watch the coda bubble screencast (alternative flash version)
(QuickTime version is approx. 23Mb, flash version is streaming)
View the demo and source code used in the screencast
HTML Markup
For the purpose of reusability, I’ve wrapped my ‘target’ and ‘popup’ in a div. The target is the element which the user must mouseover to show the popup.
<div class="bubbleInfo">
<img class="trigger" src="http://mysite.com/path/to/image.png" />
<div class="popup">
<!-- your information content -->
</div>
</div>
CSS
There’s very little to the minimum required CSS. Of course, how you markup your bubble will change this, and the screencast uses the version from the Coda web site, so there’s a considerable amount of CSS to style the bubble.
The minimum I recommend for the example is:
.bubbleInfo {
position: relative;
}
.popup {
position: absolute;
display: none; /* keeps the popup hidden if no JS available */
}
This way we can absolutely position the popup against the trigger.
jQuery
To create the effect, we need to run the following animation on the popup element:
Mouse Over
- On
mouseover: reset the position of the popup (required because we’re floating upwards as we puff away). - Animate the popup’s opacity from 0 to 1 and move it’s CSS top position by negative 10px (to move upwards).
- If the
mouseoveris fired again, and we’re still animating - ignore. - If the
mouseoveris fired again, and the popup is already visible - ignore.
Mouse Out
- Set a timer to trigger the popup hide function (this prevents accidentally moving out of the ‘active’ area).
- If a timer is set (to hide), reset the timer (thus only allowing one hide function to fire).
- Once timed out, animiate the popup’s opacity from 1 to 0 and move it’s CSS top position by negative 10px (to float upwards away).
- Set the appropriate flags to indicate the popup is now hidden.
The ‘Trick’
There was one piece of tricky logic that initially I couldn’t work out. Each time I moved the mouse over the popup, it would fire a mouseout on the trigger element - which would hide the popup. This is an undesirable bug.
There may be a another way around this, and from what I can tell, the Coda site developers didn’t solve it this way - but here’s the solution:
You need to clear the timer set in the mouseout (point 1 above) in the mouseover. This completely solves the problem.
Complete Source Code
Here’s the complete source code for the effect, including comments throughout the code to explain what each block is doing.
$(function () {
$('.bubbleInfo').each(function () {
// options
var distance = 10;
var time = 250;
var hideDelay = 500;
var hideDelayTimer = null;
// tracker
var beingShown = false;
var shown = false;
var trigger = $('.trigger', this);
var popup = $('.popup', this).css('opacity', 0);
// set the mouseover and mouseout on both element
$([trigger.get(0), popup.get(0)]).mouseover(function () {
// stops the hide event if we move from the trigger to the popup element
if (hideDelayTimer) clearTimeout(hideDelayTimer);
// don't trigger the animation again if we're being shown, or already visible
if (beingShown || shown) {
return;
} else {
beingShown = true;
// reset position of popup box
popup.css({
top: -100,
left: -33,
display: 'block' // brings the popup back in to view
})
// (we're using chaining on the popup) now animate it's opacity and position
.animate({
top: '-=' + distance + 'px',
opacity: 1
}, time, 'swing', function() {
// once the animation is complete, set the tracker variables
beingShown = false;
shown = true;
});
}
}).mouseout(function () {
// reset the timer if we get fired again - avoids double animations
if (hideDelayTimer) clearTimeout(hideDelayTimer);
// store the timer so that it can be cleared in the mouseover if required
hideDelayTimer = setTimeout(function () {
hideDelayTimer = null;
popup.animate({
top: '-=' + distance + 'px',
opacity: 0
}, time, 'swing', function () {
// once the animate is complete, set the tracker variables
shown = false;
// hide the popup entirely after the effect (opacity alone doesn't do the job)
popup.css('display', 'none');
});
}, hideDelay);
});
});
});
Taking it Further
This effect could be perfected by changing the initial reset (popup.css()) code to read from the trigger element and approximate it’s position. In my example, I’ve hardcoded it because I only have one on the page - but you may want to use this effect several times across your page.
You should follow me on Twitter here I tweet about jQuery amongst the usual tweet-splurges!
Related screencasts
Demo
If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.
Source Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Coda Bubble Example</title>
<style type="text/css" media="screen">
<!--
* {
margin: 0;
padding: 0;
}
body {
padding: 10px;
}
h1 {
margin: 14px 0;
font-family: 'Trebuchet MS', Helvetica;
}
p {
margin: 14px 0;
font-family: 'Trebuchet MS', Helvetica;
}
.bubbleInfo {
position: relative;
top: 150px;
left: 100px;
width: 500px;
}
.trigger {
position: absolute;
}
/* Bubble pop-up */
.popup {
position: absolute;
display: none;
z-index: 50;
border-collapse: collapse;
}
.popup td.corner {
height: 15px;
width: 19px;
}
.popup td#topleft { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-1.png); }
.popup td.top { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-2.png); }
.popup td#topright { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-3.png); }
.popup td.left { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-4.png); }
.popup td.right { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-5.png); }
.popup td#bottomleft { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-6.png); }
.popup td.bottom { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-7.png); text-align: center;}
.popup td.bottom img { display: block; margin: 0 auto; }
.popup td#bottomright { background-image: url(http://static.jqueryfordesigners.com/demo/images/coda/bubble-8.png); }
.popup table.popup-contents {
font-size: 12px;
line-height: 1.2em;
background-color: #fff;
color: #666;
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", sans-serif;
}
table.popup-contents th {
text-align: right;
text-transform: lowercase;
}
table.popup-contents td {
text-align: left;
}
tr#release-notes th {
text-align: left;
text-indent: -9999px;
background: url(http://jqueryfordesigners.com/demo/images/coda/starburst.gif) no-repeat top right;
height: 17px;
}
tr#release-notes td a {
color: #333;
}
-->
</style>
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
<!--
$(function () {
$('.bubbleInfo').each(function () {
var distance = 10;
var time = 250;
var hideDelay = 500;
var hideDelayTimer = null;
var beingShown = false;
var shown = false;
var trigger = $('.trigger', this);
var info = $('.popup', this).css('opacity', 0);
$([trigger.get(0), info.get(0)]).mouseover(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
if (beingShown || shown) {
// don't trigger the animation again
return;
} else {
// reset position of info box
beingShown = true;
info.css({
top: -90,
left: -33,
display: 'block'
}).animate({
top: '-=' + distance + 'px',
opacity: 1
}, time, 'swing', function() {
beingShown = false;
shown = true;
});
}
return false;
}).mouseout(function () {
if (hideDelayTimer) clearTimeout(hideDelayTimer);
hideDelayTimer = setTimeout(function () {
hideDelayTimer = null;
info.animate({
top: '-=' + distance + 'px',
opacity: 0
}, time, 'swing', function () {
shown = false;
info.css('display', 'none');
});
}, hideDelay);
return false;
});
});
});
//-->
</script>
</head>
<body id="page">
<h1>Coda Bubble Example</h1>
<p>This shows a demonstration of the 'puff' popup bubble effect as seen over the download link on the <a href="http://www.panic.com/coda/">Coda web site</a>.</p>
<p>Roll the mouse over and out from the download image to see the popup fade in and out of view, while gently gliding upwards.</p>
<p><a href="http://jqueryfordesigners.com/coda-popup-bubbles">Read the article, and see the screencast this demonstration relates to</a></p>
<p><small>Note that the transparency doesn't work in IE - this is the same on the Coda web site where the images were sourced.</small></p>
<div class="bubbleInfo">
<div>
<img class="trigger" src="http://jqueryfordesigners.com/demo/images/coda/nav-download.png" id="download" />
</div>
<table id="dpop" class="popup">
<tbody><tr>
<td id="topleft" class="corner"></td>
<td class="top"></td>
<td id="topright" class="corner"></td>
</tr>
<tr>
<td class="left"></td>
<td><table class="popup-contents">
<tbody><tr>
<th>File:</th>
<td>coda 1.1.zip</td>
</tr>
<tr>
<th>Date:</th>
<td>11/30/07</td>
</tr>
<tr>
<th>Size:</th>
<td>17 MB</td>
</tr>
<tr>
<th>Req:</th>
<td>Mac OS X 10.4+</td>
</tr>
<tr id="release-notes">
<th>Read the release notes:</th>
<td><a title="Read the release notes" href="./releasenotes.html">release notes</a></td>
</tr>
</tbody></table>
</td>
<td class="right"></td>
</tr>
<tr>
<td class="corner" id="bottomleft"></td>
<td class="bottom"><img width="30" height="29" alt="popup tail" src="http://static.jqueryfordesigners.com/demo/images/coda/bubble-tail2.png"/></td>
<td id="bottomright" class="corner"></td>
</tr>
</tbody></table>
</div>
</body>
</html>

Play QuickTime version
Play Flash version

TVP On 29th July 2008 at 06:07
Great tutorial…thank you!
Can anyone please help with creating multiple instances of the popups? I’ve tried everything I can think of and no luck.
Thanks!
chuck On 31st July 2008 at 02:07
i see a lot of questions about multiple instances on the same page. i have that very same question. is this something that can be done, and if so, how? or do i need to find another solution? i have to have multiple instances. thanks, very very cool!
jordan On 31st July 2008 at 18:07
great tutorial, thank you!
my problem is that i have multiple instances and each instance has a dynamically-sized popup. namely, the height won’t be known until the user hovers on the link (using AJAX).
how can i grab the mouse coords, then use those to position the popup (minus the height of the popup)? sorry if this question is beyond the scope of your tutorial.
thanks in advance for any suggestions!
Görsel Dersler On 1st August 2008 at 09:08
great tutorial, thank you!
Steve On 3rd August 2008 at 00:08
Great tutorial, thanks a lot! You’ve just got yourself another subscriber :)
hups On 7th August 2008 at 15:08
hello where can i make multi bubbles ??
Brendan On 10th August 2008 at 03:08
Clearly a great tutorial. I would however echo some of the other comments on here asking for a slightly extended version showing how we could use multiple instances on the same page. You can see such an effect at http://www.mediatemple.net
How could we reproduce this kind of thing?
Thanks in advance.
typoman On 10th August 2008 at 16:08
really great description. thanks a lot!
arief On 12th August 2008 at 01:08
thank you very much for the tutorial, it inform me a lot :)
adam On 14th August 2008 at 21:08
Anyone figure out how to get the PNG transparency to work in IE7? I dont understand why the alpha doesn work in 7 for this when it supports PNG.
Remy On 14th August 2008 at 22:08
@Adam - the images the Panic chaps made don’t have the right flavour of transparency. None the less, you don’t want to take their images directly, you should look to create something unique for your site or solution.
adam On 15th August 2008 at 16:08
@Remy - Thanks for the tip, but I did create a custom image, using same PNG recipe I usually do, and it doesnt fully load the alpha in IE7, so Ive put a conditional in place that uses a GIF in IE6,7 instead. Normally I would only have to implement the less desirable alt. in IE6. Mind if I email you the URL for the site in dev and the PNG image? Use the left logic form? Could Ryan be onto something that the setinterval never stops the image from animating and therefore IE7 cant load the alpha channel?
Slava On 23rd August 2008 at 08:08
Hello, can’t view quicktime screentask, it requires some kind of codec ((( that’s why can’t get how to implement mltiple bubbles. D’you have any complete tutorial about multiple bubbles?
Kasi Prasad On 25th August 2008 at 15:08
I’ve got a scenario that requires bubbles which when activated may be displayed on top of another bubble-able link. Problem that I’m having is that the bubble-able links are visible through the displayed bubble. Anyone know how to get around this? Changing the z-index of the link didn’t seem to work for me. Any help is appreciated.
Brendan On 29th August 2008 at 03:08
There didn’t seem to be any advice forthcoming about creating mulitple bubble popups. So, I did it myself. I’ve just finished a tutorial about how I did it which can be seen at http://www.tokyorepublik.com/blog/
I hope I’m not stepping on anyone’s toes here but there seems to be a fairly big demand for it.
Cheers guys. Top site.
Akhilesh On 4th September 2008 at 07:09
ok i wanted something pop like clicking , when u click the button pop up comes and when u click again it goes offf.
here is the code
//jquery file
$(function () { $(’.bubbleInfo’).each(function () { var distance = 10; var time = 25; var hideDelay = 500;
it works fine !!!!!
wecreateyou On 11th September 2008 at 16:09
Thanks for sharing Brendan,
I was wondering if you could suggest how to mashup your solution with Jquery-Corners found here >> http://plugins.jquery.com/project/corners.
Thanks again
Pryde On 12th September 2008 at 09:09
Thanks for this! I found it on the smashing magazine site and plan to use it on our new site. Excellent!
Michael On 16th September 2008 at 13:09
I just wanted to tell that i launched a site featuring several ideas from this blog: http://www.moossen.net/
and i have a question to the lawyers reading this: is it legal to reuse the original background images from panic.com for the popup??
and one more tip, i had to use the fixpng plugin and apply the background fix also for ie7… if not it looks awful (as the original at panic.com).
Remy On 16th September 2008 at 14:09
@Michael - I suspect there’s not a lot of lawyers reading this site - but here’s my advice: I would strongly advice you create your own images for your web site. This a) create your own unique style, and b) there’s no issue of taking copy written images.
Michael On 16th September 2008 at 18:09
@Remy - Thanks for the hint, but just to clear/discuss the copyright of these ‘very small images’ and I do not want to be nasty, nevertheless it seems to me that the same applies also to you and even more, since you are also using those images for your great tutorials, catching readers and finally getting hired… on the other side, my site is dedicated to my family so i doubt i will have ever never more than 200 visitors that are not really interested in gadgets, it is just me having fun with and learning JQuery… i would really appreciate your (and anybody’s) comments…
Remy On 16th September 2008 at 18:09
@Michael - the same does apply (well, my point (b) at least) to this web site. You’re right, and I think (I can’t remember) but I believe I mention this very point in the screencast.
It’s a double edged sword for me because I’m trying to demonstrate how to implement a particular technique (as such need to replicate the effect as close as possible) whilst yet promoting good practises such as being unique and creative.
Either way, we do what we believe is right - and anyone from Panic or otherwise, please feel free to get in touch you’ve anything to add :-)
Michael On 16th September 2008 at 19:09
@Remy - Yeah… it is a tricky issue… i just hope they do not knock our doors with legal demands ;) but i am considering to change the graphics, to learn also a little bit photoshop, it is just the time and not so much fun for me ;)
Rob Q. On 25th September 2008 at 22:09
Ugghh - got this working great but fails with IE 7 with ugly black border. I tried png fix and could not correct issue like Michael did at http://www.moossen.net/.
How about a tutorial on how to make this work with IE? It’s brilliant otherwise, but I can’t use it as is.
Michael On 26th September 2008 at 20:09
Somebody contacted me today to ask me about the PNG background transparency fix i used for IE7, so here it is:
first, i used this script: http://jquery.andreaseberhard.de/pngFix/jquery.pngFix.js
But it worked in ie6 but not in ie7 for me (and i just realized today, that the same also applies to ie8) as it is, so i just tried to apply the background fix also to ie7 and it worked out. :)
All you have to do is to move the background fix code to its own IF block: - remove this code from lines 88 to 96. - add them at the end in a separate IF block applying to all IE versions:
Then just apply it as in the documentation, ie:
HTH