Before last week, I had thought that making a sticky navigation would require a custom script and take a bit of time. By “sticky” it means an menu or element that is not always on top of the page but then sticks there after you scroll past a certain Y position. That was until I found jQuery Waypoints which makes it very easy to trigger the change in the element’s CSS position value and make it look as it stays on the top of the page.
Unfortunately it is not the end of the challenge. From jQuery Waypoint’s example and other tutorials I found, they only show how to apply it when you have horizontal elements. In my case, I wanted to use it for Bluelounge’s products menu which has vertical orientation and quite long. The way sticky menu works is by changing the CSS position from relative to fixed and our menu’s height is 854px. This means if the viewport is less than that, the bottom area of the menu will not be viewable at all.
The idea was to keep the “default behavior” when scrolling past the assigned waypoint but then change the CSS position value when it is near the end of the page, before the footer. With this approach, visitors can scroll through the bottom of the page and see the rest of the menu. Note that our product pages are mostly not too long, so this solution could still be considered. If you have very long pages, then I would not recommend it because your users will have to scroll a lot before they can see the rest of the menu.
So, how to achieve this? We need to create two waypoints: one at the start of the content (after header) and one at the end of the content (before footer).
The Codes
Here is the simplified version of page HTML structure:
[syntax_prettify linenums=”linenums”]
<div class="wrapper" id="products">
<div class="container_16">
<div class="header">
<!– Header Content –>
</div>
<div class="pagecontent sidebar">
<div class="grid productlist"> <!– wrapper for menu –>
<!– start: Product Menu –>
<ul id="list-products">
<li class="category"><h3>Cable Management</h3></li>
<li class="cable-management"><a href="/products/sumo/" title="Sumo: A Heavyweight for small cables"><span>Sumo</span></a></li>
<li class="cable-management"><a href="/products/cableboxmini/" title="CableBox Mini: Liven up your space"><span>CableBox Mini</span></a></li>
<li class="cable-management"><a href="/products/cableclip/" title="CableClip: Sleek, minimalist design solution for managing cables of all sizes"><span>CableClip</span></a></li>
<li class="category"><h3>Desks</h3></li>
<li class="desks"><a href="/products/studiodesk/" title="StudioDesk: A traditional workspace with a modern twist"><span>StudioDesk</span></a></li>
</ul>
<!– end: Product Menu –>
<div class="clear"></div>
</div>
<div class="grid details">
<!– Page Content –>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
<div class="footer">
<!– Footer Content –>
</div>
[/syntax_prettify]
And here is the JS code:
[syntax_prettify linenums=”linenums”]
$(‘.productlist’).waypoint({
handler: function(direction) {
$(‘#list-products’).toggleClass(‘sticky-top’); //change position to fixed by adding ‘sticky-top’ class
}
});
$(‘.footer’).waypoint({
offset: $(‘#list-products’).height()+20, //calculate menu’s height and margin before footer
handler: function(direction) {
$(‘#list-products’).toggleClass(‘sticky-top’); //remove ‘sticky-top’ class
$(‘.productlist’).toggleClass(‘sticky-bottom’); //change position to absolute for the wrapper
}
});
[/syntax_prettify]
Finally the simplified CSS code:
[syntax_prettify linenums=”linenums”]
.sticky-top {
position: fixed;
top: 0;
}
.sticky-bottom {
position: absolute;
bottom: 0;
}
[/syntax_prettify]
The Logics
As I have mentioned, Waypoints is really making things easy by allowing you to set your ‘anchors’ within the page. So in line 1 of the JS, when the scroll hits <div class="productlist"> it will toggle a new class sticky-top for the menu which changes the CSS position to fixed and make it stick to the top of the page.
The harder part is to tell the script that after reaching another waypoint, it has to remove the sticky-top class from the menu and make the menu’s wrapper stick to the end of the page’s content. To do this, we first need to calculate the position of the other waypoint. For this, we have to make use the offset option.
From Waypoints documentation:
Offset
This option determines how far the top of the element must be from the top of the viewport to trigger the callback function.
Since the <div class="footer"> is the element that we want to set as the new waypoint, we have to add the offset with the height of the menu by using jQuery’s height() function. I also added an extra 20px because there is 20px bottom margin from the page content to the footer.
Real-life usage
You can see the result on Bluelounge product page. Inside the product page, there are also other scripts running like the tabs and masonry so I have to add additional steps to make all of them work nicely together. But I am not going to the details and will save it for another time.
The technique is not perfect yet, you can probably still notice the page jumps a little. Not to mention a certain minimum height of the viewport is still required, which is half of the menu, so on very small desktop screens this will not work optimally. For now, I am quite happy with this and welcoming any suggestion to improve it further.
Note: New Bluelounge website launched in the beginning of 2015, therefore the result link above is no longer working.