Headroom.js
Give your pages some headroom. Hide your header until you need it.
Installation
Download here (sizes shown are after gzipping):
Or install via npm/yarn:
npm install headroom.js --save
yarn add headroom.js
What's it all about?
Headroom.js is a lightweight, high-performance JS widget (with no dependencies!) that allows you to react to the user's scroll. The header on this site is a living example, it slides out of view when scrolling down and slides back in when scrolling up.
Why use it?
Fixed headers are a popular approach for keeping the primary navigation in close proximity to the user. This can reduce the effort required for a user to quickly navigate a site, but they are not without problems…
Large screens are usually landscape-oriented, meaning less vertical than horizontal space. A fixed header can therefore occupy a significant portion of the content area. Small screens are typically used in a portrait orientation. Whilst this results in more vertical space, because of the overall height of the screen, a meaningfully-sized header can still be quite imposing.
Headroom.js allows you to bring elements into view when appropriate, and give focus to your content the rest of the time.
How does it work?
In response to scroll events, headroom.js adds and removes CSS classes to an element:
<!-- initially -->
<header class="headroom">
<!-- scrolling down -->
<header class="headroom headroom--unpinned">
<!-- scrolling up -->
<header class="headroom headroom--pinned">
Relying on CSS classes affords headroom.js great flexibility. The choice of what to do when scrolling up or down is now entirely yours - anything you can do with CSS you can do in response to the user's scroll.
Usage
Headroom.js has a pure JS API, plus an optional jQuery plugin and AngularJS directive.
Include the headroom.js
script in your page, and then:
// if you're using a bundler, first import:
import Headroom from "headroom.js";
// grab an element
var myElement = document.querySelector("header");
// construct an instance of Headroom, passing the element
var headroom = new Headroom(myElement);
// initialise
headroom.init();
Options
Headroom.js can also accept an options object to alter the way it
behaves. You can see the default options by inspecting
Headroom.options
. The full structure of an options object
is as follows:
var options = {
// vertical offset in px before element is first unpinned
offset : 0,
// or you can specify offset individually for up/down scroll
offset: {
up: 100,
down: 50
},
// scroll tolerance in px before state changes
tolerance : 0,
// or you can specify tolerance individually for up/down scroll
tolerance : {
up : 5,
down : 0
},
// css classes to apply
classes : {
// when element is initialised
initial : "headroom",
// when scrolling up
pinned : "headroom--pinned",
// when scrolling down
unpinned : "headroom--unpinned",
// when above offset
top : "headroom--top",
// when below offset
notTop : "headroom--not-top",
// when at bottom of scroll area
bottom : "headroom--bottom",
// when not at bottom of scroll area
notBottom : "headroom--not-bottom",
// when frozen method has been called
frozen: "headroom--frozen",
// multiple classes are also supported with a space-separated list
pinned: "headroom--pinned foo bar"
},
// element to listen to scroll events on, defaults to `window`
scroller : someElement,
// callback when pinned, `this` is headroom object
onPin : function() {},
// callback when unpinned, `this` is headroom object
onUnpin : function() {},
// callback when above offset, `this` is headroom object
onTop : function() {},
// callback when below offset, `this` is headroom object
onNotTop : function() {},
// callback when at bottom of page, `this` is headroom object
onBottom : function() {},
// callback when moving away from bottom of page, `this` is headroom object
onNotBottom : function() {}
};
// pass options as the second argument to the constructor
// supplied options are merged with defaults
var headroom = new Headroom(element, options);
CSS
Recall that headroom.js works by adding and removing CSS classes in response to the user's scroll. This means you will need some CSS to achieve the effect you want. For example, you could hide the header on scroll down, and reveal it again on scroll up. The most basic CSS for this would be:
.headroom--pinned {
display: block;
}
.headroom--unpinned {
display: none;
}
Whilst this is functional, it's a bit of a jarring effect. We could do better with some CSS transitions to smoothly move the header in and out of view:
/**
* Note: I have omitted any vendor-prefixes for clarity.
* Adding them is left as an exercise for the reader.
*/
.headroom {
will-change: transform;
transition: transform 200ms linear;
}
.headroom--pinned {
transform: translateY(0%);
}
.headroom--unpinned {
transform: translateY(-100%);
}
Notice that we're transitioning the transform
property.
The reason to transition this is because
transforms are much cheaper to animate
compared to top
, bottom
etc. Another benefit
is that translation percentage values are relative to the dimensions
of the element, whereas top
etc. percentage values are
relative to the dimensions of the viewport. This means you don't need
to know the height of the element in advance, -100%
will
always slide the element entirely out of view.
The CSS above is enough to get you started, but feel free to try something entirely different. Let you imagination run wild. But not too wild, your users won't appreciate that.
Methods
The following methods are available to be called on a headroom instance:
-
destroy()
: destroy the headroom instance, removing event listeners and any classes added -
pin()
: forcibly set the headroom instance's state to pinned -
unpin()
: forcibly set the headroom instance's state to unpinned -
freeze()
: freeze the headroom instance's state (pinned or unpinned), and no longer respond to scroll events unfreeze()
: resume responding to scroll events
Dealing with anchor links
Clicking on anchor links (links within the same page) causes scrolling of the page. If this causes a scroll upwards, Headroom will pin the header. This can cause the linked element to be obscured by the header.
To avoid this problem we can define
scroll-padding
in CSS, which is designed for just this purpose:
html,
body {
scroll-padding-top: 65px; /* set to the height of your header */
}
Usage with jQuery
Include the headroom.js
and
jQuery.headroom.js
scripts in your page, and then:
// simple as this!
// NOTE: init() is implicitly called with the plugin
$("#header").headroom();
// or with options
$("#header").headroom(options);
The plugin also offers a data-* API if you prefer a declarative approach.
<!-- selects $("[data-headroom]") -->
<header data-headroom></header>
<!-- or with options -->
<header data-headroom data-tolerance="5" data-offset="205"></header>
Usage with AngularJS
Include the headroom.js
and
angular.headroom.js
scripts in your page, and require the
headroom
module in your AngularJS application module.
Then:
<header headroom></header>
<!-- or -->
<headroom></headroom>
<!-- or with options -->
<headroom tolerance="0" offset="0" scroller=".my-scroller" classes="{pinned:'headroom--pinned',unpinned:'headroom--unpinned',initial:'headroom'}"></headroom>
Examples
Head over to the headroom.js playroom if you want see some example uses. There you can tweak all of headroom's options and try out various CSS effects in an interactive demo.
Browser support
Headroom.js is dependent on the following browser APIs:
All of these APIs are capable of being polyfilled, so headroom.js can work with less-capable browsers if desired. Check the linked resources above to determine if you must polyfill to achieve your desired level of browser support.
If you do not want to polyfill each of the above APIs, you can test for browser support before initialising. This avoids errors in older browsers and treats headroom.js as a progressive enhancement:
if(Headroom.cutsTheMustard) {
var hr = new Headroom(element);
hr.init();
}
Changelog
v0.12.0
- Feature: support separate up/down values for offset (thanks to Matthew Nessworthy)
v0.11.0
- Feature: support multiple classes (thanks to Nestor Vera)
v0.10.4
v0.10.3
- Bugfix: scroll-y values were not rounded, causing some calculations to be off
- Bugfix: incorrect logic for calculating top state
v0.10.2
v0.10.0
- Community: hacknug has kindly offered to assist in project maintenance
- Performance: switch to passive scroll event listeners (thanks to Ned Baldessin)
- Feature: Add freeze/unfreeze methods (thanks to Andrei Victor Bulearca)
- Housekeeping: modernise codebase and toolings
- Bugfix: incorrectly detecting scroll to bottom on non-window scrollers
- Feature: support importing on server for SSR (thanks to Andrzej)
- Bugfix: support using an iframe's window as scroller
- Performance: optimise and reduce bundle size
v0.9.4
- Correctly remove all classes on destroy (thanks to sweetroll)
v0.9.3
- Correctly destroy headroom instance in angular directive (thanks again to Nikita Korotaev)
v0.9.2
- Fix bugs with angular directive (thanks to Nikita Korotaev)
v0.9.1
- Add minified files to bower package
v0.9.0
- Improvements to angular directive (thanks to Todd Motto)
- Add bower.json to npm package
v0.8.0
- Now available on npm as
headroom.js
! - Fix attempting to use function binding before feature test (thanks to Rajiv Makhijani)
- Add classes and callbacks for scrolling to and from bottom of page
- Improve element selection in angular directive (thanks to Simon)
- Fix bug where some classes not removed on destroy (thanks to Anton Burkovsky)
- Added UMD support
- Correctly destroy headroom instance in angular directive (thanks to Kenneth Crawford)
v0.7.0
v0.6.0
- Up/down tolerance can be specified separately (thanks to Chip Lay)
v0.5.0
- Classes added above and below offset, with corresponding callbacks (thanks to Andreas Andreou)
v0.4.0
- Options merged with defaults, even with plain JS
- Callbacks for pinning and unpinning
- AngularJS plugin (thanks to Pitr Vernigorov)
- UX improvements (thanks to David Kaneda)
v0.3.11
- Various bug fixes
License
Licensed under the MIT License.