How to Hide/Reveal a Sticky Header on Scroll (With JavaScript)
Have you ever seen one of those fixed (or “sticky’) header bars which disappear when you begin scrolling down the page, then reappear whenever you start to scroll back up? In this exercise we’ll learn how to build our own using a bit of JavaScript.
Why?
Sticky components (such as headers) are extremely popular in the world of web design; they keep certain essential UI elements permanently in view, and easily accessible should users need them. However, under certain circumstances (if the headers hold lots of content, or the viewport size and orientation limit the amount of available screen space) sticky headers can be obtrusive.
“When implemented wrong, sticky navigation elements can serve as a distraction from the actual content.” – Aaron Andre Chichioco
A sticky header which disappears from view when not needed (ie: when the user is scrolling to see more content) is an excellent compromise.
We can achieve this kind of effect by using an external library like Headroom.js, but we’re going to learn the mechanics of what’s underneath by building something ourselves. As a bonus, we’ll also make the header menu fully functional, ready for you to add your own customization.
What We’re Building
Here’s what we're going to create (scroll to test the behavior):
Let’s get started!
1. Begin With the Page Markup
To start off our sticky header we’ll open the markup with a header
which contains a nav
. Within it, we’ll put the menu toggle button and the menu itself. Just for enriching the page with some dummy content, we’ll also define three full-screen sections. We’ll add a few background images to them taken from a previous tutorial.
Here’s the page markup:
<header class="page-header"> <nav> <div class="trigger-menu-wrapper"> <button class="trigger-menu"> <svg width="20" height="20" viewBox="0 0 24 24"> <path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z"/> </svg> <span>MENU</span> </button> </div> <ul class="menu">...</ul> </nav> </header> <main class="page-main"> <section>...</section> <section>...</section> <section>...</section> </main>
2. Add the Sticky Header CSS
Let’s add some CSS rules to improve the way our header looks and (to a degree) behaves.
For the sake of simplicity, I won’t walk through the initial reset styles, but feel free to look at them by clicking at the CSS tab of the demo project.
The header styles are pretty straightforward, but two things are important:
- Firstly, both the toggle menu wrapper and the menu will be fixed positioned elements.
- Secondly, the menu will initially be hidden.
The styles related to the header are as follows:
/*CUSTOM VARIABLES HERE*/ .trigger-menu-wrapper { position: fixed; top: 0; left: 0; right: 0; display: flex; justify-content: center; padding: 10px; z-index: 2; background: var(--lightpurple); transition: transform 0.4s; } .page-header .trigger-menu { display: flex; align-items: center; font-size: 1.5rem; color: var(--white); } .page-header .trigger-menu svg { fill: var(--white); margin-right: 5px; transition: transform 0.3s; } .page-header .menu { position: fixed; top: 0; left: 0; right: 0; bottom: 0; display: none; text-align: center; padding: 15vh 0 5vh; overflow: auto; z-index: 1; } .page-header .menu a { font-size: 3rem; } .page-header .sub-menu a { font-size: 1.5rem; }
The sections will behave as full-screen elements with a background image and a dark overlay on top of it. These will give us something to scroll past so we can see the hide/reveal behavior of our header:
.page-main section { position: relative; background-repeat: no-repeat; background-position: center; background-size: cover; height: 100vh; } .page-main section::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.25); }
3. Add the JavaScript
As a next step, let’s add some behavior to the menu.
Toggle Menu
Each time we click on the toggle button, the menu’s visibility will change. If it’s hidden, it’ll appear. But if it’s visible, it’ll disappear.
We’re handling this in quite a rudimentary way, but it gives you the potential to tailor things to your liking. Here’s the required JavaScript code:
const body = document.body; const triggerMenu = document.querySelector(".page-header .trigger-menu"); triggerMenu.addEventListener("click", () => { body.classList.toggle("menu-open"); });
And the related styles:
.page-header .trigger-menu svg { transition: transform 0.3s; } .menu-open { overflow: hidden; } .menu-open .page-header .menu { display: block; } .menu-open .page-header svg { transform: rotate(45deg); }
As you may have noticed, there isn’t any animation during the changes of the menu’s state. That happens because I used the non-animatable display
property. If you want to add some kind of animation, replace this property with something animatable like opacity
or visibility
.
Toggle Header
Let’s now turn our attention to something more interesting.
Each time we scroll down, the toggle button (and the header in general) should disappear with a slide-out animation. If we then scroll up, it should appear with a slide-in animation.
To implement this functionality, we’ll use two helper classes: scroll-up
and the scroll-down
. More specifically:
- As we scroll down, the
body
will be given thescroll-down
class. - As we scroll up, it’ll be given the
scroll-up
class. - If we scroll to the top of the page, it will lose its
scroll-up
class.
To detect the scrolling direction, we’ll store the last scroll position in a variable (lastScroll
). Initially the value of this variable will be 0. Then as we scroll, we’ll check if the new position is greater than or less than the old one. Based on the result of that condition, we’ll apply the corresponding class to the body
.
Here’s the JavaScript code to handle that:
const body = document.body; const nav = document.querySelector(".page-header nav"); const menu = document.querySelector(".page-header .menu"); const scrollUp = "scroll-up"; const scrollDown = "scroll-down"; let lastScroll = 0; window.addEventListener("scroll", () => { const currentScroll = window.pageYOffset; if (currentScroll == 0) { body.classList.remove(scrollUp); return; } if (currentScroll > lastScroll && !body.classList.contains(scrollDown)) { // down body.classList.remove(scrollUp); body.classList.add(scrollDown); } else if (currentScroll < lastScroll && body.classList.contains(scrollDown)) { // up body.classList.remove(scrollDown); body.classList.add(scrollUp); } lastScroll = currentScroll; });
And the associated styles:
/*CUSTOM VARIABLES HERE*/ .trigger-menu-wrapper { transition: transform 0.4s; } .scroll-down .trigger-menu-wrapper { transform: translate3d(0, -100%, 0); } .scroll-up .trigger-menu-wrapper { transform: none; } .scroll-up:not(.menu-open) .trigger-menu-wrapper { background: var(--lightpurple); box-shadow: 0 0 10px rgba(0, 0, 0, 0.35); }
Conclusion
That’s it, folks! During this exercise we learned how to toggle a sticky header’s visibility depending on the scrolling direction.
I hope you found this tutorial useful and that you’ll take advantage of it in your upcoming projects. As always, thanks a lot for reading!
More Sticky Tutorials
Stick around! There’s plenty more to learn about sticky behavior in web design:
-
PatternsHow to Create a Fixed Header Which Animates on Page Scroll
-
WordPressHow to Make a Sticky Menu in WordPress
-
CSSSticky Positioning with Nothing but CSS
-
UI DesignHow to Create a “Sticky” Floating Video on Page Scroll
from Envato Tuts+ Tutorials
Comments
Post a Comment