In this post, I'll outline a comparison of "sticky" methods to make website components stay on a page.

Sticky? What in the Sam Hill are you talking about?

I am not sure if you’ve heard, but every site needs some sticky components. At least, that seems like an on-going trend in web development. Just in case you haven’t run into this term before, sticky is the methodology of keeping a part of the site - possibly the navigation, header, or social share toolbar - from scrolling off the page, making it stick as the user scrolls down the page.

How did I get here?

Recently, when a coworker had to leave a project suddenly to adopt a child, I suddenly had the task of making a sticky header work according to the client’s requirements. This sticky header requirement had quite a few moving parts. There were a few different components in the header that needed to change as the page scrolled.

  • The logo shrunk in size, and its top and bottom padding decreased as it was placed sticky at the top.
  • The top graphic bar of the header and the secondary menu needed to scroll off or disappear.
  • The search bar needed to become sticky at a higher point on the page than its initial position.
  • The main navigation needed to stick under these components. 

“Let’s do this thing!”

For demonstration purposes, let's use the following markup.

<div class="header-wrapper">
 <div class="top-graphic-bar">
   <div class="inside-wrapper">I’m the top graphic bar</div>
 </div>
 <div class="logo">
   <div class="inside-wrapper">Awesome Logo</div>
 </div>
 <div class="secondary-menu">
   <div class="inside-wrapper">Secondary | Menu | I should disappear</div>
 </div>
 <div class="search">
   <div class="inside-wrapper">Search anything - not really, it’s a demo.</div>
 </div>
 <div class="main-navigation">
   <div class="inside-wrapper">I am | the main | navigation | for this | site</div>
 </div>
</div>

There are 5 components: top graphic bar, logo, secondary menu, search, and main navigation. There is a wrapper around all of this. There is also an inside wrapper for each component. After applying some css, (you can find my demonstration code at JSFiddle), you get the initial view below.

sticky page jumps initial

I am going to use the stickyjs to make items sticky. This library is instantiated by calling sticky on a jQuery object and passing in options. For more information, see the documentation on github

The First Method - Making the Header Sticky

In looking at the markup and the requirements, I initially thought it made the most sense to call sticky on the header wrapper.  

$('.header-wrapper').sticky({
        wrapperClassName: 'sticky',
        className: 'stuck',
        topSpacing: 0
    });

Then, since that adds a class when sticky is activated, that is, when the user scrolls to the object’s top minus the supplied top parameter, I can use the added class to adjust the interior components.

The top graphic bar should disappear.
CSS before sticky is activated Added CSS after sticky is activated
.top-graphic-bar {
    width: 100%;
    background-color: black;
}
.stuck .top-graphic-bar {
    display: none;
}
 
The logo should shrink.
CSS before sticky is activated Added CSS after sticky is activated
.logo {
    background-color: red;
    margin: 25px 10%;
    width: 30%;
    height: 70px;
    padding-top: 35px;
    float: left;
}
.stuck .logo {
    height: 30px;
    margin: 10px 10%;
    width: 20%;
    padding: 10px;
}
The secondary menu should disappear.
CSS before sticky is activated Added CSS after sticky is activated
.secondary-menu {
    background-color: blue;
    width: 40%;
    margin: 30px 2% 5px;
    float: left;
    padding: 10px;
}
.stuck .secondary-menu {
    display: none;
}

The search and main menu should move up without additional css since items above are being removed or shrunk down.

So, after scrolling, the header is updated to the following display.

Great! I’m done. It works!

sticky page jumps when scrolled

Except, when you scroll, the page jumps and the transition is so abrupt that it feels broken.

sticky jumps

You could add some css transitions and maybe change the sticky top to negative, but wow that gets complex quickly. I found that calling sticky on the items separately made much more sense.

The Second Method - Make Individual Components Sticky

Using the same markup, I updated the JavaScript to the following:

$('.logo div').sticky({
  wrapperClassName: 'sticky',
  className: 'stuck',
  topSpacing: 10
});
    
$('.search div').sticky({
  wrapperClassName: 'sticky',
  className: 'stuck',
  topSpacing: 8
});

$('.main-navigation div').sticky({
  wrapperClassName: 'sticky',
  className: 'stuck',
  topSpacing: 52
});

This update changes the selectors needed to update my styles when a component becomes sticky. The top graphic bar will simply scroll off the page naturally since it is no longer inside a sticky component.

For the logo, the positioning needs to be separate from the sticky selector on the inside div.

CSS before sticky is activated Added CSS after sticky is activated
.logo {
    margin: 25px 10%;
    width: 30%;
    float: left;
}
 
.logo .inside-wrapper {
    background-color: red;
    padding: 30px 0;
    margin: 0;    
}
.logo .stuck .inside-wrapper {
    padding: 10px;
}

The secondary menu will also naturally scroll off the page.

The search will need similar separation and adjustment as the logo.

CSS before sticky is activated Added CSS after sticky is activated
.search {
    width: 35%;
    margin: 5px 2% 5px 7%;
    float: left;
}
 
.search .inside-wrapper {
    background-color: orange;
    padding: 10px;
}
.search .stuck .inside-wrapper {
    padding: 2px 10px;
}

The main navigation just needs the separation. The sticky functionality will take care of the rest. 

CSS before sticky is activated Added CSS after sticky is activated
.main-navigation {
    width: 100%;
    float: left;
}
 
.main-navigation .inside-wrapper {
    background-color: green;
    padding: 10px;
}
 

Now, the components become sticky so smoothly and beautifully. There is just one problem:

sticky smooth background

Since individual components were made sticky instead of the overall header, the content shows behind the sticky components. Since this is not what we wanted, let’s make a zero height div that also becomes sticky. When it becomes sticky, it will cover the background of the other sticky components.

I add this to the markup just below the top graphic bar:

<div class="header-background"></div>

I add this to my JavaScript:

$('.header-background').sticky({
        wrapperClassName: 'background-sticky',
        className: 'stuck',
        topSpacing: 0
    });

I add the following to my CSS:

.header-background {
    width: 100%;
    height: 0;
}

.stuck .header-background {
    height: 52px;
    z-index: 0;
    background-color: white;
}

To make sure my new background doesn’t cover up the header components before they become sticky, I add this also to the CSS:

.sticky div, .secondary-menu {
    z-index: 1;
    position: relative;
}

Now, when the content scrolls, this element will hide the content scrolling behind the sticky components.

final sticky component

If you would like to see this in action, feel free to access the example here: https://jsfiddle.net/tnathanjames/nv4ttnxc/.