First things first, I do not claim to be an React/Gatsby expert. The following content is just something I figured out by trial-and-error and would like to document / share with other React/Gatsby hackers.
See the nice auto-hiding Navbar on this site? That took me a while to implement. Not that it's a difficult task, but it's was rediculously hard to make it perform correctly (or shall I say, elegantly) across browsers/platforms. Basically, I want it to auto-hide when you are scrolling down, and auto-reveal when you are scrolling up. Every bit of srenn real estate matters, especially in a mobile-first world.
First let's get some context out of the way: I'm using Bootstrap with React, so Reactstrap is the default way to go. You could either have a fixed navbar or a non-fixed one, but not an auto-hiding/auto-revealing one. If you ever have found yourself typing something like "reactstrap auto hiding navbar" into Google, you're probably in a similar situation. Fortunately, there are many hacks that sort of works available as open-source. However, since you are using React, you probably don't want to mix any jQuery stuff into your code (my take is you really shouldn't), so packages written with jQuery like Bootstrap Auto-Hiding Navbar are out of consideration. And unfortunately, that rules out almost all of them.
I tested out a couple of solutions. One of them works almost perfectly for me, which is available at How TO - Hide Menu on Scroll - W3Schools.com. If you try it out in Chrome or Firefox, it works like a charm, which concluded my first attempt to build this fancy navbar.
However, if you happen to be using Safari on a Mac, problems starts to reveal. And it sucks a lot: when you scroll to the very bottom, the navbar will "magically" reveal itself; when you scroll back to the very top, the navbar will "magically" disappear. Something like the following:
And this undersirable effect also applys to mobile browsing using Safari.
The cause of this effect is that Safari allows "over-scroll" and will display a bouncy effect once you do so. I'm guessing it's a UX sugar? Anyway, in this case, it's super annoying.
To resolve this, you have to carefully come up with edge fixes for Safari. Here's what I end up putting in my ./header.js
:
import React, { Component } from 'react';
import './header.css';
import { Container, Collapse, Navbar, NavbarToggler, Nav, NavItem } from 'reactstrap';
... ... // Other imports
const ListLink = (props) => (
// ListLink implementation omitted
);
class Header extends Component {
constructor (props) {
super(props);
this.toggle = this.toggleNavbar.bind(this);
// https://www.w3schools.com/howto/howto_js_navbar_hide_scroll.asp
if (typeof window !== 'undefined') {
let prevScrollpos = window.pageYOffset;
window.onscroll = function () {
const maxScroll = document.body.clientHeight - window.innerHeight;
let currentScrollPos = window.pageYOffset;
if (
(maxScroll > 0 && prevScrollpos > currentScrollPos && prevScrollpos <= maxScroll)
|| (maxScroll <= 0 && prevScrollpos > currentScrollPos)
|| (prevScrollpos <= 0 && currentScrollPos <= 0)
) {
document.getElementById("navbar").style.top = "0";
} else {
document.getElementById("navbar").style.top = "-5.0rem"; // adjustable based your need
}
prevScrollpos = currentScrollPos;
}
}
}
render () {
return (
<div>
<Navbar id="navbar" {/* other attributes omitted*/}>
... ... // Navbar implemetation omitted
</Navbar>
</div>
);
}
}
export default Header;
and ./header.css
:
# navbar {
transition: top 0.5s
}
which is based on the W3Schools.com solution mentioned above.
To check the source code in full context: header.js and header.css.
Now, everything looks perfect, everywhere. I'm not going to put a gif here when you can simply scroll the page yourself to test it out.
🤪
Happy hacking. Michael out.