Adding Headroom with JavaScript
Fixed navigation bars have an annoying drawback — they take up precious screen
real estate. We can add headroom to any web page by automatically retracting and
expanding the navigation bar on
We will create a super simple implementation with vanilla JavaScript
and
discuss some ways to improve its behavior. First let’s add some
hacker style boilerplate to our app.js
file.
javascript
/**
* Boilerplate Functions
*/
function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }
function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
function arem (a, x) { var i = apos(x, a); if (i >= 0) { a.splice(i, 1); } return a; }
function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
function addClass (el, cl) { if (el) { var a = el.className.split(' '); if (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }
function remClass (el, cl) { if (el) { var a = el.className.split(' '); arem(a, cl); el.className = a.join(' ') } }
Then setup our variables. The variable previousPosition
stores the initial
page position. Bind to the navigation bar with the navbar
selector and grab
its height using the offsetHeight
.
javascript
/**
* Setup Variables
*/
var previousPosition = window.pageYOffset;
var navbar = document.getElementById("navbar");
var navbarHeight = navbar.offsetHeight;
Add an onscroll
event with a simple check that hides the navigation bar on
scroll down and reveals it on scroll up.
javascript
/**
* Headroom Action
*/
window.onscroll = function() {
var currentPosition = window.pageYOffset;
if (previousPosition > currentPosition) {
remClass(navbar, 'headroom');
} else if (currentPosition > navbarHeight) {
addClass(navbar, 'headroom');
}
previousPosition = currentPosition;
};
We add headroom to the page only when the current position is greater than the navigation bar’s height.
javascript
else if (currentPosition > navbarHeight) {
addClass(navbar, 'headroom');
}
For our CSS
style sheet, force the navigation bar upwards from its initial
fixed position and give it a transition time to make the movement less jarring.
css
.headroom { top: -5em !important; }
.navbar {
-webkit-transition: top 0.75s;
-o-transition: top 0.75s;
transition: top 0.75s;
}
Improvements
How can we improve this simple script? Let’s introduce velocity. We will show the navigation bar only if the user scrolls up faster than normal.
The velocity will be the difference between the previous and current position. The greater the difference, the faster the user scrolls. A scroll downwards produces a negative value and a scroll upwards produces a positive value.
javascript
var velocity = previousPosition - currentPosition;
Now that we know the velocity, we can add resistance by setting a velocity
threshold of 55
. The user must now overcome this velocity to reveal the
navigation bar. The effect of this resistance threshold will vary based on
device and browser.
The else if
condition is capped to velocity < 0
. This handles a situation
where the user performs a flick scroll gesture which involves both acceleration
and deceleration. In this scenario accidental triggers can happen if we do not
consider the direction of the scroll.
javascript
if (velocity > 55 || currentPosition < navbarHeight) {
remClass(navbar, 'headroom');
} else if (velocity < 0) {
addClass(navbar, 'headroom');
}
Due to this implementation, add an initial check at run time to handle a refresh
anywhere in the middle of the
javascript
var previousPosition = window.pageYOffset;
var navbar = document.getElementById("navbar");
var navbarHeight = navbar.offsetHeight;
if (previousPosition > navbarHeight) {
addClass(navbar, 'headroom');
}
window.onscroll = function() {
var currentPosition = window.pageYOffset;
var velocity = previousPosition - currentPosition;
if (velocity > 55 || currentPosition < navbarHeight) {
remClass(navbar, 'headroom');
} else if (velocity < 0) {
addClass(navbar, 'headroom');
}
previousPosition = currentPosition;
};
Conclusion
That’s it. We can make even