Hartley Brody

Creating a "Sticky" Sidebar Widget that Scrolls With You

Moments after I finished my last how-to post on adding subtle pop-ups to your website, I was asked about the sliding widget at the bottom of the sidebar on Fresh on Campus.

As I was building the layout for that site, I ran into the standard conundrum when designing layouts for blogs: you have no idea how “tall” the main column of posts will be, so it’s impossible to build a sidebar that doesn’t have one of two problems.

Either the sidebar hangs way down past the bottom of the content, making the scroll bar unnecessarily tiny, or else it ends up too short, leaving an inordinate amount of wasted blank space next to your content

What is a designer to do!?

Enter - the sticky sidebar widget! With this, you won’t feel the need to cram tons of junk into your sidebar (please don’t do that, regardless!) and you won’t have tons of wasted space.

Check out the demo page to see it in action →

The idea is that the widget normally sits in its typical spot at the end of the sidebar. It behaves normally as the visitor begins to scroll through your content, and as it first comes into view. But once the visitor starts to scroll past it and it’s about to disappear off the screen, jQuery magic kicks in and makes it “stick” to the window.

Now it stays with the visitor as they scroll further down. If they decide to scroll back up, it will stop following them once it’s back in its original location at the end of the sidebar. This way, the visitor always sees some sidebar content next to your main blog content.

Does that sound useful? Good, let’s get started…

Warning: This post gets a little geeky, so a basic proficiency with jQuery is necessary to continue. Don’t worry though, it’s not too hard and you can always just copy/paste my code if you get scared… Alright let’s dive in!


The HTML

The obvious bit of HTML that you’ll need is the content of the “sticky” div. The not-so-obvious bit of HTML that you’ll need is a secondary div that “catches” the sticky div so that it stops sticking to the window, once it’s back in its original spot.

<div id="catcher">
    <!--catcher-->
</div>
<div id="sticky">
    <!--sticky-->
</div>

If you’re using Wordpress, don’t worry about adding this markup, it’s even easier than that. Add two text widgets to the bottom of your sidebar. Inside these widgets, add <!--catcher--> and <!--sticky-->. Make sure you save the widgets. We’ll come back to them in a minute.

The jQuery

First off, we’ll revisit our isScrolledTo function, which I love reusing since it’s so simple and elegant. We’re going to modify it slightly from the last tutorial since we’re focusing on the top edge of our screen this time, not the bottom.

function isScrolledTo(elem) {
    var docViewTop = $(window).scrollTop(); //num of pixels hidden above current screen
    var docViewBottom = docViewTop + $(window).height();
    var elemTop = $(elem).offset().top; //num of pixels above the elem
    var elemBottom = elemTop + $(elem).height();
    return ((elemTop <= docViewTop));
}

Next, we want to define our “catcher” div and our “sticky” div. The catcher div should be immediately above the sticky div in our sidebar, and the sticky div should be at the bottom of our sidebar so that it doesn’t cover up other content as the visitors scroll.

If you’re using Wordpress, then these IDs are already set for you and you need to find them. Go into the source code of your website (‘right click’ > ‘view source’) and search for “catcher” and “sticky” (the text we inserted earlier). You’ll see them nested inside something that looks like <li id="text-xx" class="widget widget_text">. The text-xx are the IDs that you want to use for these.

If you’re not using Wordpress, then the IDs are whatever you specified when you added the divs earlier.

var catcher = $('#catcher');
var sticky = $('#sticky');

Now that we have defined everything, it’s time to setup our actions. We’re going to put everything inside jQuery’s $(window).scroll() function, so that our script always runs whenever the visitor is scrolling.

The first if statement checks to see if our sticky div is bumping up against the top of the screen. If it is, it’s time to break it off from the rest of the sidebar and stick it to the window with some fixed positioning.

The second if statement checks to make sure that the sticky div is never higher than the catcher div. If it is, it’s time to put the sticky div back in its place at the end of the sidebar.

Geek note: We can’t really add it back into the flow of the page without a full page reload. So we’re going to fake it by applying absolute positioning. But don’t worry. I promise, your visitors won’t notice…

$(window).scroll(function() {
    if(isScrolledTo(sticky)) {
        sticky.css('position','fixed');
        sticky.css('top','0px');
    }
    var stopHeight = catcher.offset().top + catcher.height();
    if ( stopHeight > sticky.offset().top) {
        sticky.css('position','absolute');
        sticky.css('top',stopHeight);
    }
});

We’re going to wrap all of that jQuery inside some script tags <script type="text/javascript"> and </script>. Don’t forget to load the jQuery library, otherwise none of this will work!

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>

When the jQuery is all put together it will look like something like this:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script>
$(document).ready(function() {
        function isScrolledTo(elem) {
            var docViewTop = $(window).scrollTop(); //num of pixels hidden above current screen
            var docViewBottom = docViewTop + $(window).height();
            var elemTop = $(elem).offset().top; //num of pixels above the elem
            var elemBottom = elemTop + $(elem).height();
            return ((elemTop <= docViewTop));
        }
        var catcher = $('#catcher');
        var sticky = $('#sticky');
        $(window).scroll(function() {
            if(isScrolledTo(sticky)) {
                sticky.css('position','fixed');
                sticky.css('top','0px');
            }
            var stopHeight = catcher.offset().top + catcher.height();
            if ( stopHeight > sticky.offset().top) {
                sticky.css('position','absolute');
                sticky.css('top',stopHeight);
            }
        });
    });
</script>

You should put this somewhere in your site’s footer, before the closing </body> tag. If you’re using Wordpress, no need to worry about any additional HTML markup. Just add some content to the two text widgets from your admin dashboard.

If you’re building your sidebar manually, make sure you add the HTML markup that matches the catcher and sticky divs we specified earlier.

Enjoy your new, perfect-fit sidebar! But first, read the fine-print:

NOTE: Since the sticky div can’t be scrolled past, it’s important to think about visitors with smaller screen sizes. If the div is too tall and it hangs off the bottom of their screen, they’ll never be able to see the content at the bottom of the widget.

NOTE: If you have a tall footer on your website, then you might notice some overlap issues when you scroll all the way to the bottom of your site. You’ll have to specify the footer div as a second “catcher” and then add two more if statements to the $(window).scroll() area. I’ll let you hack around with this if you need it cause I’m feeling lazy right now… ;-) Update: More information on using the sticky sidebar with a tall footer can be found on the Wordpress support forums. (Check out the demo here.)

If you end up using this code on your site, I’d love to see it in action! Send me a tweet and let me know!