Saturday, June 21, 2014

Sticky Nav Bars and Top-of-Page Jumpers

Ever visit a website where if you start to scroll then certain elements on a page will stick to a particular location, like a navigation bar that stays put at the top of the page? Sometimes even a link will also appear from a side of the document which allows the user to jump back to the top of the page with a single click.

Well that's what this tutorial is about. In fact the concepts I'll be discussing can actually be taken a step further and you'll be able to create other "sticky" elements on your page (like a social media link list that can slide into view).

With this walk-through I'm assuming you're somewhat familiar with jQuery and CSS, so as with pretty much all things jQuery please download the library if you want to try this for yourself. Overall this example is really easy, so even with minimal experience it should be a snap to get this working on your site.

First let's take a look at how this works. Click here to view the example and then scroll down the page to see how the nav bar locks in place; also look for the TOP link to appear on the right-hand side of the document towards the bottom (you might need to resize your browser window to make it smaller if you can't scroll).

Now let's examine the HTML markup. Overall, there's nothing really that special one has to do here, so I'm only going through the notable elements.

<div id="top"><a href="#topofpage">TOP</a></div>

As you can probably guess, the above is the link that appears when you scroll down the page a certain distance. It's pretty much safe to put this line anywhere, as in the CSS rule (shown later) it is defined with a fixed position and moved off screen. More on that later.

Next up is the nav element (if you're not using HTML5 then a div could just as easily be used) with an id of "navbar", which will be useful when we target it with jQuery (although if you just have one nav element, then you could skip the id part). Overall the menu (the unordered lists) is as one would expect most menus to be; there is also a sub-list that is basically the dropdown.

At this point, I'm getting right into the jQuery and CSS styles, as the interaction between the two is key.

The first line in the code is:

$(window).scroll(function() {

and what it does is binds the anonymous function (a function with no name) to the scroll event handler. In other words, when the user starts scrolling on the webpage, that function will execute its code block. So what's in this block?

if (window.pageYOffset > 100) {
        $("#navbar").addClass("fixed");
        $("article").addClass("articlepadding");

        $("#top").css("right", -6 + "px");

} else {
        $("#navbar").removeClass("fixed");
        $("article").removeClass("articlepadding");

        $("#top").css("right", -75 + "px");
}

That's it... that's actually the whole thing. Let's read through it. There's an if statement that examines how far the page has scrolled vertically using the pageYOffset property of the built-in JavaScript window object. If that offset is greater than 100 pixels, so if the user has scrolled the document a distance of 100 pixels, then those three commands in the if statement are executed. By the way, there is a compatibility note at the bottom of this post if you want to ensure that IE versions eight and below will work with this getup; I'm mean, I usually ignore most IE users (please switch to another browser if you're using IE!).

Where did I get this 100 value from? It is the height of the header as defined in its CSS rule. The way I've designed this particular example is that if the user scrolls the header out of view, I want the nav bar to lock into place at that exact moment. So if your header is a different size, then keep the height and that value in the if statement the same. If you have a header that changes its height dynamically, thus no specific value, then you can use the offsetHeight property of the element in the if statement instead.

So if the pageYOffset is greater than 100 pixels, a CSS class called "fixed" is added to the nav bar, and another CSS class called "articlepadding" is added to the article element on the page. The "fixed" class looks like this:

.fixed {
        width: 80%;
        position: fixed;
        top: 0;
        box-shadow: 2px 2px 10px black;
}

I've also defined a CSS rule for the nav element. Think of this as its default style:

nav {
        height: 25px;
        background: #00a;
       
        -webkit-transition: all .3s ease-out;
        -moz-transition: all .3s ease-out;
        -ms-transition: all .3s ease-out;
        -o-transition: all .3s ease-out;
        transition: all .3s ease-out;
}

The nav rule doesn't specify a position type for the nav element, thus the default static positioning is used. But when the "fixed" class is applied, it changes the positioning to the fixed type of course and that's basically what keeps the nav bar glued to the top of the document. Notice also the width has to be defined as well as the top position. I added the box-shadow in as an effect which beautifully animates due to the nav rule having the transition property set. Cool hey? We can use CSS to do the animation work for us; CSS automatically applies any transitions it needs to as we change the classes (or other CSS properties of elements) via jQuery.

And how about the classes for the article?

article {
        background-color: white;
}
   
.articlepadding {
        padding: 25px 0 0 0;
}

Similar to the nav element, the article has a basic rule with nothing more than a background color applied. The "articlepadding" class, added by jQuery when the document is scrolled more than 100 pixels, sets a top padding of 25 pixels. Why 25? Because it is the same as the height of the nav bar. Go ahead and comment out that padding line. Notice that odd "jump" as you scroll down? When the nav bar is set to fixed, it is removed from the normal flow of the document. Thus, the article element fills that void where the nav element used to be and "jumps" up 25 pixels. So the padding (could also be margin) compensates for the nav bar being removed from the normal document flow.

Now we're at the last line in the if statement's code block:

$("#top").css("right", -6 + "px");

When you use the css function in jQuery, you are basically getting or setting an inline CSS style. In this case, we are changing the right property of the div with the id of "top" and changing it to -6 pixels. There's also a css rule defining this element in general:

#top {
        position: fixed;
        background-color: #00a;
        border: 1px solid white;
        border-radius: 5px;
        width: 68px;
        height: 25px;
        right: -75px;
        top: 80%;
        box-shadow: 2px 2px 10px black;
       
        -webkit-transition: all .3s ease-out;
        -moz-transition: all .3s ease-out;
        -ms-transition: all .3s ease-out;
        -o-transition: all .3s ease-out;
        transition: all .3s ease-out;
}

Most of this is decoration. The important declarations are the position set to fixed, right set to -75px (keeps the element hidden just off the page) and the transition. The top set to 80% is the vertical position of the element; the percent unit keeps it relatively in the same spot, even if the browser window is resized.

So as you can probably tell at this point, when the user scrolls the distance of the header, jQuery changes the right position to -6px (use whatever value you want though for your specific needs). Because of the CSS transition declaration, we see a pretty animation occur rather than a sudden jump.

I believe that most people can figure out the else portion of the JavaScript code. If the user does not scroll the page more than 100 pixels, then we ensure the classes are removed from their respective elements and the right position of the "top" div is reset to -75px.

Easy.

And about that compatibility for IE browsers versions eight or less? If you checked out the documentation for window.pageYOffset (linked here and earlier) you'll see the the wonderful folks at Mozilla have provided some code to help make this as compatible as possible with older IE browsers. So for this example I could have added in this line before the if statement, with a minor update to the condition:

var verticalOffset = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

if (verticalOffset > 100) {...}

Not only have you learned how to create sticky elements on a page, you've also seen how CSS transitions can help bring life to elements being moved around or that have various other properties changed via JavaScript.

Remember, you can make suggestions for future tutorials and I'll be happy to consider them; just leave a comment. L8r!

No comments:

Post a Comment

You may comment, but if it's spam or advertising, I'll be deleting your comment and blocking your arse!