
Controller for Bootstrap navbar
Javascript
class NavbarController {
constructor(options) {
this.prevScrollPos = window.scrollY;
this.navbar = document.querySelector('.navbar');
this.collapse = document.querySelector('.navbar-toggler');
this.mobileMenu = document.querySelector('.navbar-collapse');
this.navbarLogo = document.querySelector('.navbar-logo');
this.scrollThreshold = options.scrollThreshold || 80;
this.backToTopThreshold = options.backToTopThreshold || 400;
this.backToTopHide = options.backToTopHide || 'right: -150px;';
this.backToTopShow = options.backToTopShow || 'right: 36px;';
this.navbarBackground = options.navbarBackground || 'bg-body';
this.toggleShadow = options.toggleShadow || true;
this.behavior = this.getBehavior();
this.backToTopSelector = options.backToTopSelector || '.back-to-top';
this.backToTop = document.querySelector(this.backToTopSelector);
this.attachEvents();
}
getBehavior() {
if (this.navbar) {
if (this.navbar.classList.contains('slide-up')) {
return 'slideUp';
} else if (this.navbar.classList.contains('hidden')) {
return 'hideDown';
} else if (this.navbar.classList.contains('bg-transparent')) {
return 'transparent';
} else if (this.navbar.classList.contains('none')) {
return 'none';
} else {
return 'shrink';
}
} else {
return 'transparent';
}
}
attachEvents() {
window.addEventListener('scroll', () => {
this.handleScroll();
});
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener('click', (e) => {
e.preventDefault();
this.smoothScrollToAnchor(anchor.getAttribute('href'));
});
});
if (this.backToTop) {
this.backToTop.addEventListener('click', (e) => {
e.preventDefault();
this.scrollToTop();
});
}
}
handleScroll() {
const currentScrollPos = window.scrollY;
if (currentScrollPos > this.backToTopThreshold) {
this.backToTop.style = this.backToTopShow;
} else {
this.backToTop.style = this.backToTopHide;
}
if (this.navbar) {
switch (this.behavior) {
case 'hideDown':
this.hideDownShowUp(currentScrollPos);
break;
case 'slideUp':
this.slideUp(currentScrollPos);
break;
case 'transparent':
this.transparent(currentScrollPos);
break;
case 'shrink':
this.shrink(currentScrollPos);
break;
default:
break;
}
}
this.prevScrollPos = currentScrollPos;
}
smoothScrollToAnchor(anchorLink) {
if (anchorLink === '#') return;
const anchorID = document.querySelector(anchorLink);
if (!anchorID) {
console.error(`debug: ${anchorLink} does not exist on the page`);
return;
}
let offset = this.navbar.offsetHeight;
if (
this.navbar.offsetTop === 0 &&
this.mobileMenu.classList.contains('show')
) {
offset -= this.mobileMenu.offsetHeight;
}
window.scrollTo({
top: anchorID.offsetTop - offset,
behavior: 'smooth',
});
this.collapse.click();
}
scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
hideDownShowUp(currentScrollPos) {
this.navbar.classList.toggle(
'hidden',
this.prevScrollPos < currentScrollPos
);
}
slideUp(currentScrollPos) {
const shouldShow = currentScrollPos > this.scrollThreshold;
this.navbar.classList.toggle('slide-up', !shouldShow);
this.navbar.classList.toggle('slide-down', shouldShow);
}
transparent(currentScrollPos) {
const shouldTransparent = currentScrollPos > this.scrollThreshold;
this.navbar.classList.toggle('bg-transparent', !shouldTransparent);
this.navbar.classList.toggle(this.navbarBackground, shouldTransparent);
if (this.toggleShadow) {
this.navbar.classList.toggle('shadow', shouldTransparent);
}
}
shrink(currentScrollPos) {
const shouldShrink = currentScrollPos > this.scrollThreshold;
this.navbarLogo.classList.toggle('shrunk', shouldShrink);
this.navbar.classList.toggle(this.navbarBackground, shouldShrink);
if (this.toggleShadow) {
this.navbar.classList.toggle('shadow', shouldShrink);
}
}
}
How to use the class
document.addEventListener('DOMContentLoaded', function () {
const navbar = document.querySelector('.navbar');
const logo = document.getElementById('navbarLogo');
const options = {
scrollThreshold: 80,
backToTopThreshold: 400,
backToTopSelector: '.back-to-top',
backToTopHide: 'right: -150px;',
backToTopShow: 'right:36px;',
navbarBackground: 'bg-info-subtle',
toggleShadow: true,
};
if (navbar) {
new NavbarController(options);
}
if (logo) {
new NavbarController(options);
}
});
CSS
main {
padding-top: 60px;
}
.navbar, .navbar-logo {
transition: all 820ms ease-in-out;
}
.back-to-top {
position: fixed;
top: calc(100vh - 72px);
bottom: 5px;
right: -150px;
transition: all 820ms ease-in-out;
}
@media (min-width: 576px) {
.hidden {
opacity: 0;
}
}
@media (min-width: 576px) {
.slide-down {
top: 0;
}
}
@media (min-width: 576px) {
.slide-up {
top: -150px;
}
}
@media (min-width: 576px) {
.navbar-logo {
height: 80px;
width: 80px;
}
}
@media (min-width: 576px) {
.navbar-logo.shrunk {
height: 40px;
width: 40px;
}
}