iPhone-like Sliding Headers
Posted on 10th September 2009 — The iPhone has a few unique UI features, one in particular are the static headings when you’re scrolling through a list, so you know the context of the content. We’ll see how to create this effect using jQuery.

Watch
Watch iPhone-like Sliding Headers screencast (Alternative flash version)
QuickTime version is approximately 68Mb, flash version is streaming.
View the demo used in the screencast
The Effect
I can’t show you an example in the wild so the screenshot below will have to do for now. As the user swipes through a list, the header for that section of content remains visible at the top of the window. This gives the user context in what they’re looking at.
One really nice UI effect is when a new heading is just about to replace the existing heading, it pushes the existing heading out of view. It’s very subtle and only really visible if you perform the scroll slowly. Unfortunately, for now, we’re not replicating subtle part of the effect.
What we will do, is when the user is scrolling through an overflowing block of content, the heading for the visible block of content remain at the top. You can see the effect in action in the demo.
Creating the Effect
To create this effect we need to have a fixed position fake header sitting over the content. To do this we’re going have to mess around with the DOM using jQuery. We need to wrap the box with another box of the same height and width and give it
position: relativeso that our fake header can make use ofposition: absoluteand appear to be fixed.Now that this is sitting at the very top level, we need to bring the real headers up over the fake header, so that it appears as if they’re pushing the fake out of the way. To achieve this we’ll use a
z-indexon the real headers that is higher than the fake header. However, this causes it’s own problems.All the headers now have to be
position: absoluteto really sit over the fake header, which also means we have to give the element a fixed height and width. This isn’t too much of a problem because we can get this information from the original.Now that the headers are position absolute, the text that is sitting next to it falls flush against each other, because the newly positioned headers don’t flow in the document. To fix this we need to create a spacer element. In the screencast I mention this might be possible to simplify. You could duplicate the header and insert it after the original header before setting the
position: absolute. To be consistent with the screencast, I’ve stuck with creating the spacer.Finally we need to attach an event handler to the containing box, to say when there is a heading that is exactly aligned with the top of the containing box, to switch in the text from that header in to the fake and move the fake’s
z-indexto be at least one more than the current header (so it sits topmost).jQuery
The jQuery job breaks down in to four parts:
The completed example is also available if you want to skip through each step.
Variables
We need to grab jQueryified versions of the container box and headers. We also need to create a clone of the first heading for the fake header. Finally we initialise a
z-indexand store the top position of the container. At first it looks like.offset().topwould do, but we also need to factor in themargin-topandborder-top-width, and this gives us the real top position.Of course the whole thing is wrapped in the
$(document).ready()method to ensure the code only runs once the DOM is ready.Inserting the Fake Header
This is a pretty straight forward process:
boxclass name so that it’s the same width and height, but more importantly:position: relative$fakeHeadervariableAbsolutely Positioning Headings
Since we’re absolutely positioning the headings we’ll need to manually reset the width of the element. We’re also setting a constantly incrementing
z-indexthat the fake header can borrow from to jump above the real heading.Once the headings are absolutely positioning, they no longer affect the flow of the document, and the adjacent elements now sit flush against each other. Now we need to manually correct this issue just using a spacer element. I’ve created a new empty
divelement and set the height and width to theouterHeightandouterWidthof the heading. It’s important that we select theouterHeightrather than justheightbecause we need to include the margin around that element.I’d suggest that if you’re using this technique in a live environment, you can either do it using code (as I have done in this example), or if you’re finding that it doesn’t match up 100%, you can create a class in your CSS that prepares that spacer, then apply the class to the newly inserted
div.Using the Scroll Event to Trigger the Effect
As the user scrolls the overflowing container element, we need to track where our fake header is, and once it passes underneath a real header, the fake header will match the text and use a high
z-index.To achieve this, we bind a scroll event to the container element, and as it is being scrolled, we loop through the headings checking it’s
topposition.If the
topposition is less than thetopposition of the container (remember we included margin and border width to accurately ascertain this), then we copy that heading’s details across to the fake heading.The effect that we achieve is that as the fake header passes under the real header, as soon as they’re in the same location visually on the page, the fake header pops over the real header giving the illusion that the heading is now locked in position.
That’s all we need. As you’ll see with any of these tutorials, we just need to break the task in to smaller tasks and apply the solutions a bit at a time.
Check out the final iPhone-like Sliding Headers demo and let me know if you implement this technique in a real web site in the wild.
A Note About IE
IE8 is fine and matches this effect perfect. Of course IE6 & IE7 have to put their boot in, but only a little. There’s one subtle difference in IE6 & 7 due to a bug in their
z-indexstack model. The bug is that the fake header doesn’t pass underneath, but over the top. I’ve had people look at the effect in IE and say nothing’s wrong – but now that I’ve pointed it out to you, you’ll see where the error is.I know playing with the
position: relativewill help, but I’m not 100% sure how to fix this just yet. So I challenge you, dear reader, to see if you can get it working in IE7. Good luck – I have hope in you!You should follow me on Twitter here I tweet about jQuery amongst the usual tweet-splurges!