Watch
Watch jQuery Fixed Floating Elements screencast (Alternative flash version)
QuickTime version is approximately 45Mb, flash version is streaming.
View the demo used in the screencast
Understanding the effect
When I scrolled down the Apple store when I had something in my basket I found that the summary and what I had selected would follow me down the page. I’ve seen this effect before, but often it would be a little jumpy and the element would have to catch up with the scrolling.
Apple’s version was very smooth and didn’t jump when I kept scrolling.

Upon firing up Firebug I can see that once the scrollbar gets to the point where the basket is at the top of the page, the basket has a class applied to it which gives it a position: fixed - which explains why it holds still while I continue to scroll down.
Other ways to do it
Like I said, I’ve seen this effect before, for example on Simon Willison’s web site (see the comments form) (one of the speakers for my Full Frontal conference).
When I scroll down the page, the comments form can sometimes “play catchup” with the scrolling position, because I suspect the top position is being recalculated (though I’ve not checked his code to be sure or not).
So I’ve taken the style of Simon’s site for the example and added the jQuery we need to make this a nice smooth effect.
Caveat: IE6
Since we’re solving this effect using position: fixed, IE6 doesn’t support this CSS property. I’m not saying that IE6 doesn’t matter, but I’m suggesting that this effect isn’t a requirement to be able to interact with the site properly, so if IE6 users don’t see this extra effect, I’m okay with this. As I explained in the screencast, you’ll need to decide this yourself, check your site’s demographic, whether it’s a personal project, etc.
Markup & CSS
The trick really happens in the CSS here and being able to flip back and forth between absolute positioning and fixed positioning. So I’ve prepared the layout as such. You guys and gals being designer and front-end types will know how you’ll want to style the elements.
One trick I did find was that I had to wrap the element that would receive position: fixed in a wrapper with position: absolute so that the left position was again the wrapper rather than the body element. The effect of not having the wrapper, meant that when I switched fixed on the element, it jumped to the left by the amount of padding and margin set on the body element.
I’ve simplified the markup form the live example
<style>
/* required to avoid jumping */
#commentWrapper {
left: 450px;
position: absolute;
margin-left: 35px;
width: 280px;
}
#comment {
position: absolute;
top: 0;
/* just used to show how to include the margin in the effect */
margin-top: 20px;
border-top: 1px solid purple;
padding-top: 19px;
}
#comment.fixed {
position: fixed;
top: 0;
}
</style>
<div id="comments">
<ol>
<li>Here be the comments from visitors...</li>
<li>etc...</li>
</ol>
</div>
<div id="commentWrapper">
<div id="comment">
<form>
<!-- take their response -->
</form>
</div>
</div>
jQuery
The jQuery required is very simple to create this effect. Obviously first up, include jQuery:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
First of all, we need to capture the initial position of the comments form. There’s a few parts to this to ensure we get a real number (rather than NaN - not a number), and to ensure we also calculate in the margin-top:
var top = $('#comment').offset().top;
Next we subtract the margin-top (I’ve used marginTop in the code below, but they’re interchangeable) of our element:
var top = $('#comment').offset().top - $('#comment').css('marginTop');
However, the result from the CSS method is “20px”, so we need this as a number so we can subtract properly, we’ll use parseFloat to achieve this:
var top = $('#comment').offset().top - parseFloat($('#comment').css('marginTop'));
Finally, if we don’t include a margin in the CSS, the result of the CSS method call is auto, which won’t parse properly, so we need to replace the text “auto” with the number 0:
var top = $('#comment').offset().top - parseFloat($('#comment').css('marginTop').replace(/auto/,0));
This all needs to be done inside once the document has loaded, so we’ll wrap all of this in the ready method, and while we’re at it we need to bind an event handle when the user scrolls down the page, so we’ll attach this when the page has loaded:
$(document).ready(function () {
var top = $('#comment').offset().top - parseFloat($('#comment').css('marginTop').replace(/auto/,0));
$(window).scroll(function () {
// let's do something funky
});
})
Within the scroll event handler, we need to:
- Capture the current scrolled Y position using
$(window).scrollTop() - If the comment element is above the Y position, add the
fixedclass - Otherwise remove the class
Note that since we bind the scroll event to the window object, I’ve used $(this).scrollTop() (since this is also the window in this case).
All of this put together results in:
$(document).ready(function () {
var top = $('#comment').offset().top - parseFloat($('#comment').css('marginTop').replace(/auto/, 0));
$(window).scroll(function (event) {
// what the y position of the scroll is
var y = $(this).scrollTop();
// whether that's below the form
if (y >= top) {
// if so, ad the fixed class
$('#comment').addClass('fixed');
} else {
// otherwise remove it
$('#comment').removeClass('fixed');
}
});
});
And that’s how to create a fixed floating element or sidebar and make it super smooth when you scroll.
There is some optimisation you could do to this code, but for a simple effect this should work nicely for you.
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 HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Fixed Floating Elements</title>
<style>
body {
color:#000;
font-family:"Helvetica Neue",helvetica,sans-serif;
line-height:1.4em;
}
#comments {
float:left;
width:450px;
}
#comment-wrapper {
position: relative;
}
#commentWrapper { /* required to avoid jumping */
left: 450px;
position: absolute;
margin-left: 35px;
width:280px;
}
#comment {
position: absolute;
top: 0;
margin-top: 20px;
border-top: 1px solid purple;
padding-top: 19px;
}
h2 {
font-family:georgia,serif;
}
#comments ol li {
border-top: 1px solid purple;
}
#comments ol li:first-child {
border-top: 0;
}
#comment.fixed {
position: fixed;
top: 0;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script>
$(function () {
var msie6 = $.browser == 'msie' && $.browser.version < 7;
if (!msie6) {
var top = $('#comment').offset().top - parseFloat($('#comment').css('margin-top').replace(/auto/, 0));
$(window).scroll(function (event) {
// what the y position of the scroll is
var y = $(this).scrollTop();
// whether that's below the form
if (y >= top) {
// if so, ad the fixed class
$('#comment').addClass('fixed');
} else {
// otherwise remove it
$('#comment').removeClass('fixed');
}
});
}
});
</script>
</head>
<body>
<h1>A blog, just like Simon's</h1>
<h2 class="band">9 comments</h2>
<div id="comment-wrapper">
<div id="comments">
<ol>
<li>
<div class="comment" id="c53607">
<p>dolore magna aliqua Ut enim, ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo. </p>
<p>id est laborum Lorem ipsum, dolor sit amet, consectetur adipisicing elit sed do eiusmod tempor incididunt ut labore et. </p>
</div>
</li>
<li>
<div class="comment" id="c53618">
<p>sunt in culpa qui officia, deserunt mollit anim.</p>
<p>minim veniam quis nostrud exercitation, ullamco laboris nisi, ut aliquip ex ea commodo consequat Duis aute irure dolor in. reprehenderit in voluptate velit esse, cillum dolore eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident. </p>
<p>deserunt mollit anim id est, laborum Lorem ipsum, dolor sit amet consectetur adipisicing elit sed do eiusmod tempor incididunt. ut labore et dolore magna, aliqua Ut enim ad.</p>
</div>
</li>
<li>
<div class="comment" id="c53620">
<p>qui officia,</p>
<p>eu fugiat nulla pariatur. Excepteur'sint occaecat cupidatat non proident sunt in culpa.</p>
<p>aliquip ex ea commodo consequat Duis. aute irure dolor in reprehenderit in voluptate? velit'esse cillum dolore.</p>
<p>exercitation ullamco laboris nisi ut!</p>
</div>
</li>
<li>
<div class="comment" id="c53627">
<p>officia deserunt - mollit anim'id est laborum Lorem'ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod, tempor incididunt ut labore et dolore' magna aliqua Ut enim ad minim veniam quis nostrud?</p>
</div>
</li>
<li>
<div class="comment" id="c53628">
<p>nisi ut aliquip ex ea...commodo consequat Duis aute irure dolor in reprehenderit...in voluptate velit, esse, cillum dolore eu fugiat nulla "pariatur Excepteur sint". occaecat cupidatat...non'proident sunt in culpa qui.</p>
</div>
</li>
<li>
<div class="comment" id="c53629">
<p>amet, consectetur adipisicing elit. sed do eiusmod tempor incididunt ut labore; et dolore magna aliqua Ut enim ad minim veniam quis nostrud exercitation ullamco laboris.</p>
<p>aute irure dolor in reprehenderit in voluptate velit esse (cillum dolore eu, fugiat nulla pariatur Excepteur) sint occaecat cupidatat'non proident sunt in culpa qui officia deserunt mollit (anim id est laborum Lorem ipsum dolor sit).</p>
<p>laboris nisi. ut'aliquip ex ea commodo consequat Duis. :)</p>
</div>
</li>
<li>
<div class="comment" id="c53630">
<p>id: est laborum Lorem ipsum dolor sit amet consectetur adipisicing elit sed do eiusmod tempor, incididunt ut labore et dolore magna aliqua Ut enim ad minim veniam quis nostrud exercitation ullamco.</p>
<p>fugiat: nulla'pariatur Excepteur sint. occaecat cupidatat non proident sunt in culpa qui officia. deserunt mollit anim, id'est laborum Lorem ipsum dolor sit (amet consectetur adipisicing'elit sed do eiusmod) tempor incididunt ut labore et dolore magna aliqua Ut enim ad/minim veniam. quis'nostrud exercitation ullamco laboris nisi ut aliquip ex ea-commodo consequat/Duis aute irure dolor'in reprehenderit in voluptate velit esse, cillum dolore'eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident sunt. in culpa qui officia deserunt mollit anim.</p>
</div>
</li>
<li>
<div class="comment" id="c53632">
<p>enim ad... minim veniam quis nostrud exercitation. ullamco laboris nisi ut aliquip ex ea commodo consequat Duis aute irure dolor in, reprehenderit in voluptate velit esse cillum dolore eu.</p>
</div>
</li>
<li>
<div class="comment" id="c53633">
<p>magna aliqua, Ut tempor incididunt ut labore et dolore.</p>
</div>
</li>
</ol>
</div>
<div id="commentWrapper">
<div id="comment">
<form>
<p class="formrow"><label for="yourname">Name:</label>
<input type="text" class="text" id="yourname" name="name" value=""></p>
<p class="formrow"><label for="yoururl">URL:</label>
<input type="text" class="text" id="yoururl" name="url"></p>
<p class="formrow"><textarea rows="10" cols="35" name="body"></textarea></p>
<p><input type="button" value="Preview comment"></p>
</form>
</div>
</div>
</div>
</body>
</html>

Play QuickTime version
Play Flash version

James Wong On 2nd January 2010 at 08:01
Hello there, nice post but I got a hitch in here. What if I have another element underneath ‘commentWrapper’ and I want the box to stop right on before it.
Nicolas Chevallier On 6th January 2010 at 19:01
Thanks for this piece of code. I add this to my toolbox for the project I work on. It’s simple but I never see a smooth scroll like yours, without any flickering problem.
John-David Dalton On 26th January 2010 at 17:01
The whole
parseFloat($('#comment').css('margin-top').replace(...))is way to over complicated.NaNvalues will convert to booleanfalseso this will workparseFloat($('#comment').css('margin-top')) || 0;.eriadam On 19th February 2010 at 12:02
Hi Remy, thanks for the j4d podcast, I love it! I just discovered that in Firefox 3.6 (only Windows) the scroll event is not being triggered while I’m actually scrolling, just only when I release the sidebar or slow the scrolling down. The y variable looks like this:
y = 0
y = 312
y = 772
y = 954
y = 958
And as it is not continuous (like 5-6 step at the time) it is jumping. Mouse wheel scrolling is the same: while the page is moving, the event is not triggered. Do you have any idea how to fix this?
Zander On 3rd March 2010 at 12:03
This is brilliant! I used it recently on the Prometheus Bound Books website, but I am building a new site and would really like to have the same functionality for horizontal scrolling rather than vertical. Can this be done?
Many thanks Remy for creating this tuorial and all the others.
Martin Berglund On 7th March 2010 at 17:03
I have noticed what I think is a browser related bug… :-/
If the technique in this tutorial is used on a fixed width, centered layout, the fixed element will not stay in its x-position when the browser window is resized. In the Apple Store it seems like Apple have found a solution that does not break when resizing the browser window.
Firefox plays nice, but Safari is not as cool about this. If ‘position: fixed’ is not added through jQuery, Safari plays nice too.
I am terrible at describing code and website behaviour, but I hope you understand what I am trying to explain.
You can see a demo of the bug here (centered, fixed width): http://jsbin.com/aqeji
Jason On 11th March 2010 at 08:03
Hmm, only problem is if the contents of that floating elements are longer than the height of the browser window, there is no way to ever get to the bottom of the floating element. Open it in a new window, make the browser 200px tall or something and then the button is not possible to get to. So this will be a problem on phones, etc. Should be some way to add this functionality, but I’ll look into it when it becomes a priority. Ah, looking at Apple’s just gave me the quick fix idea of if the floating element height is at any point larger than the browser height at that time, just don’t apply the effect. If anyone wants to come up with the better solution of having it scroll to the bottom of the too tall floating contents and then stop the effect (until it goes back up), that would be sweet.
Jason On 11th March 2010 at 08:03
Oh, quick fix just popped into my head after I looked at Apple’s. If the height of the floating element is taller (will need to be rechecked upon any client or ajax changes or browser resize) than the browser window width, just don’t apply the effect. Handling it better will take more time and feel free to implement it for me, anyone. :)
Golioth On 23rd March 2010 at 17:03
How about creating something James Wong, and earlier Thorsten too, suggested: switch the box from fixed back to an absolute position on reaching the bottom of a page or the top of the next element, for example a footer.
Anybody know how to achieve this? Like when reaching a given height from the bottom of the page.
Ricardo On 29th March 2010 at 16:03
Like Jason wrote before, i have a question, what if the floating element is bigger than the viewport?, well, maybe there can be a easy possible solution for this, but anyways, my real question is: what if the viewport is not very big OR what if there is a footer in the end? Is there any ways to make the floating element change state when its near the en of its container. In Apple’s page when you get to the end of the page, the floating element changes it state and stops floating. I dont know if you can understand, but if you go to the Apple store, and make the viewport smaller you can see what I mean. Please any help would be very appreciated.
Johan van de Merwe On 14th April 2010 at 21:04
Great work! Am putting it into a site I am working on and it is soooooooooooooooo smooth. Thank you!
JJ from Bad Salzuflen, Germany