Auto-Hide Sticky Header

Auto-hide sticky header is a shot that shoots down two rabbits: makes site navigation easily accessible anywhere on the page and saves content space at the same. My client, Easy Shine, was happy to have this type of header for their website. I've also implemented this technique here, on my website (you can see it when the width of your viewport is below 768 px).

Making header sticky

<header class="header" role="banner">
	<!-- ... -->
</header>
.header
{
	width: 100%;
	height: 7.5em;
	position: fixed;
	z-index: 1000;
	top: 0;
	left: 0;
}

A little bit of obvious HTML/CSS coding here, which makes the header stick to the top of the page, independently of the page scroll position. Now, how do we auto-hide the header?

Auto-hiding header

Auto-hide means hiding the header automatically when a user starts scrolling down the page and bringing the header back when a user might need it: they reach the bottom of the page or start scrolling up. There are at least two manners of hiding a header: reactive and lazy.

Reactive

It is when the header directly and instantly reacts to the page scroll. This manner may be a good small detail from the perspective of user experience because of the feel it brings. However it has a downside: it completely depends on JavaScript and because of the manner’s nature, we cannot use JS event throttling (calling a handler function only once in a custom period of time). In principle, that means making calculations on every scroll event and quite uselessly loading CPU. Luckily, in most cases this more a theoretical, but not a practical problem because the calculations are quite insignificant.

The JS algorithm changes the value of CSS property top when page scroll is performed:

//...
window.addEventListener( 'scroll', function()
{
	//...
	if( wScrollCurrent <= 0 ) // scrolled to the very top; element sticks to the top
		element.style.top = '0px';

	else if( wScrollDiff > 0 ) // scrolled up; element slides in
		element.style.top = ( elTop > 0 ? 0 : elTop ) + 'px';

	else if( wScrollDiff < 0 ) // scrolled down
	{
		if( wScrollCurrent + wHeight >= dHeight - elHeight )  // scrolled to the very bottom; element slides in
			element.style.top = ( ( elTop = wScrollCurrent + wHeight - dHeight ) < 0 ? elTop : 0 ) + 'px';

		else // scrolled down; element slides out
			element.style.top = ( Math.abs( elTop ) > elHeight ? -elHeight : elTop ) + 'px';
	}
	//...
});
//...

Lazy

This one, however, may not have that feeling of responsiveness, which depends a lot on JavaScript event throttling interval. Anyway, the manner is much more CPU-friendly and the header animation is based on CSS, which means we can make a use of our imagination.

Differently from the previous option, here the JavaScript part does not change CSS properties directly. It adds and removes CSS class name header--hidden:

//...
window.addEventListener( 'scroll', throttle( throttleTimeout, function()
{
	//...
	if( wScrollCurrent <= 0 ) // scrolled to the very top; element sticks to the top
		removeElementClass( element, elClassHidden );

	else if( wScrollDiff > 0 && hasElementClass( element, elClassHidden ) ) // scrolled up; element slides in
		removeElementClass( element, elClassHidden );

	else if( wScrollDiff < 0 ) // scrolled down
	{
		if( wScrollCurrent + wHeight >= dHeight && hasElementClass( element, elClassHidden ) ) // scrolled to the very bottom; element slides in
			removeElementClass( element, elClassHidden );

		else // scrolled down; element slides out
			addElementClass( element, elClassHidden );
	}
	//...
}));
//...

In CSS we define what the class name means:

.header
{
	-webkit-transition-duration: .5s;
	transition-duration: .5s;

	-webkit-transition-timing-function: cubic-bezier( 0.215, 0.610, 0.355, 1.000 );
	transition-timing-function: cubic-bezier( 0.215, 0.610, 0.355, 1.000 );

	-webkit-transition-property: -webkit-transform;
	transition-property: transform;
}

.header--hidden
{
	-webkit-transform: translateY( -100% );
	-ms-transform: translateY( -100% );
	transform: translateY( -100% );
}

An extra state for header

Sometimes, especially on homepage, it might be useful to have a bigger header when at the top of the page in order to grab visitor's attention. We need an additional CSS class for controlling header height:

.header--narrow
{
	height: 5em;
}

Whereas JavaScript part controls the usage of the newly defined CSS class name – it adds the class name when a page scroll starts and removes when a user reaches the page top.

// ...
window.addEventListener( 'scroll', throttle( throttleTimeout, function()
{
	// ...
	if( wScrollCurrent > elNarrowOffset ) // toggles "narrow" classname
	{
		if( !hasElementClass( element, elClassNarrow ) )
			addElementClass( element, elClassNarrow );
	}
	else removeElementClass( element, elClassNarrow );
	// ...
}));
// ...

Examples

The source of the demo consists of both pure-JavaScript (IE9+ compatible) and jQuery-dependent versions of the code. Feel free to investigate and use it.

Demo

Newsletter

In case you want to be informed about my blog posts.

RSS Feed

You can also subscribe to my RSS feed in order to follow my blog. Not using any RSS reader? You can try Readerrr.