Search

Fixed Floating Elements

Posted on 23rd October 2009 — On visiting Apple’s web site an putting items in my shopping basket, I noticed (an old effect) where the shopping basket would follow me down the page. We’ll look at how to replicate the fixed floating sidebars or elements with very little jQuery.

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.

Example of the scrolling fixed floating sidebar

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:

  1. Capture the current scrolled Y position using $(window).scrollTop()
  2. If the comment element is above the Y position, add the fixed class
  3. 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.

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>

Comments

  1. Chris Mahon On 23rd October 2009 at 14:10

    Nice effect indeed. Best implementation that I’ve seen is on Tim Van Damme’s blog http://maxvoltar.com/articles/stick-to-your-brand

  2. United Music Romania On 23rd October 2009 at 15:10

    You find something like this here http://www.unitedmusic.ro . Scroll down and look at Top of page element…

  3. EthanJ On 23rd October 2009 at 15:10

    Much appreciated. By pure coincidence I’ve been messing around with CSS for about an hour trying to EXACTLY this, to no avail.

    Then BAM! Google Reader and JQFD answers my wishes. Many thanks.

    Also… GET OUT OF MY BRAIN! D:

  4. Martin On 23rd October 2009 at 18:10

    Wow, I like your solution. I can see my self implementing this on a number of projects I’m working on! All the best and thank you!

  5. Alexandre On 23rd October 2009 at 19:10

    Hey, i did it here (http://www.ergonomie-web.be/pagination-ne-faites-pas-comme-google/) , on my own before Apple!! Do i get to become a star ? °-¨

  6. Rafael On 24th October 2009 at 01:10

    First time here, the tutorial is really helpful. I like when something fails in tutorials because i get to learn from your mistakes and then from mine when i try it. Will be here watching tutorials.

  7. Alex On 24th October 2009 at 13:10

    Very nice to see a new tutorial. Well implemented, I like the neatness of it all.

    And Chris, TVD’s blog looks like it’s just a fixed element sitting there, no JS

  8. Thorsten On 24th October 2009 at 15:10

    It’s a great effect. But it should be good to know how we can reset the fixed element to “position: absolute” when it reaches a probably existing footer. I think apple’s website is doing so, but I’m unable to create this effect.

  9. Alan Bristow On 25th October 2009 at 15:10

    Thanks lots for this Remy, perfect timing for an FAQ contents list I’ve been kicking :)

  10. Chris Mahon On 25th October 2009 at 15:10

    @Alex yea could well be, just thought it was a nice implementation :) I’ve also seen a jQuery plugin do something similar called StickyHeader but can’t seem to find the URL at the moment.

  11. Paul Wong On 26th October 2009 at 23:10

    First off, what a terrific website you’ve created here, and many thanks for sharing your knowledge. This tutorial is fantastic and your skills and knowledge leave me in awe. One bug I did see in this tutorial is the message box widens during the effect, watch it closely. FYI: I am viewing it in latest version of FireFox on a Mac.

  12. magrolino On 29th October 2009 at 10:10

    nice, great screencast - thx a lot! is there any considerable market-share of the (extremely old) IE in mac anymore? I remember it had extreme flaws with elements that were position:fixed and contained hyperlinks in it..

  13. aaron On 3rd November 2009 at 22:11

    Love the blog, is your feed broken?

  14. Alexsander Akers On 8th November 2009 at 02:11

    Is the effect always accompanied by that weird, discontinuous jump?

  15. Remy On 14th November 2009 at 01:11

    Alexsander - I think you mean from the demo iframe - try popping it out it to it’s own window and you’ll see it working just fine without a jump.

  16. Brian Jørgensen On 14th November 2009 at 01:11

    Hi, thanks for this - I like it!

    Keep up your nice stuff in here :o)

  17. Chris Schmitz On 15th November 2009 at 06:11

    Oh man, I just noticed that the other day and thought it was a pretty cool effect. I saw that they were using jQuery to do it, but didn’t actually figure out how they did it.

    Thanks for the great tip!

  18. Davidmoreen On 19th November 2009 at 21:11

    Wow this is cool, I’ve been curious how Apple has been doing this for a while. I mean I’ve looked at their source code before but they use Prototype so I have no clue what I was looking at. Also any examples that I did manage to find online where terrible, such as the element would sort of skip down the page…

  19. Casey On 26th November 2009 at 08:11

    Thanks for the tut. Looks great. I’m running into an error when trying to use in no conflict mode.

    parseFloat(jQuery(’#comment’).css(’margin-top’).replace(/auto/, 0));

    Any ideas?

  20. Matt On 29th November 2009 at 02:11

    One thing’s for sure, you’ve convinced me to get firebug :) Nice screencast.

  21. Mike Wheaton On 2nd December 2009 at 03:12

    Very clear explanation, thank you! I appreciate that you explain your thought process and leave in the debugging rather than editing it down to a plain step-by-step. This is far better for learning and reminds us that errors are normal and experimentation is key — perfect code doesn’t just appear the way most tutorials make it seem.

  22. kamal On 16th December 2009 at 19:12

    Thanks for this tut, very useful…

  23. Zoran On 16th December 2009 at 23:12

    Thank you for your nice tutorials, i am more into the backend programming, but your jQuery tutorials are the best out there and just got my attention lately. If you have time to look at the http://www.bbc.co.uk/ site they have panels in divs that you can drag and drop and swap places between and also you can edit the content in each panel. Would it be something hard to achieve or could you do a tutorial on it, please? I am sure there will be some jQuery UI implementation and i think it would be a good advanced tutorial to follow. At least the drag and drop effect would be nice. Thank you again for your time. Zoran

  24. Bijan On 17th December 2009 at 21:12

    Hi, The demo is not working in the IE6, I mean the form is not foxed in IE6. Is that so ?

  25. Nick On 21st December 2009 at 21:12

    Absolutely brilliant tutorial….by coincidence I was on apple.com today and was wondering how they had made this work so well. Great job, definately gonna be checking out the other tutorials on this site.

Comments are now closed.