<div class="squad-navbar" data-behaviour="squad-navbar">
<nav class="squad-navbar__nav-list" data-behavior="squad-navbar--nav-list" aria-label="Current player squad">
<a href="#" data-nav-to="squad-block-goalkeepers">
Goalkeepers
</a>
<a href="#" data-nav-to="squad-block-defenders">
Defenders
</a>
<a href="#" data-nav-to="squad-block-midfielders">
Midfielders
</a>
<a href="#" data-nav-to="squad-block-forwards">
Forwards
</a>
<a href="#" data-nav-to="squad-block-staff">
Staff
</a>
</nav>
</div>
Fixed navbar on scroll:
{
"data": [
{
"title": "Goalkeepers",
"id": "squad-block-goalkeepers",
"additionalClasses": ""
},
{
"title": "Defenders",
"id": "squad-block-defenders",
"additionalClasses": ""
},
{
"title": "Midfielders",
"id": "squad-block-midfielders",
"additionalClasses": ""
},
{
"title": "Forwards",
"id": "squad-block-forwards",
"additionalClasses": ""
},
{
"title": "Staff",
"id": "squad-block-staff",
"additionalClasses": ""
}
]
}
/* eslint-disable no-param-reassign */
const MOBILE_BREAKPOINT = 800;
const initFixedNavbar = parentElement => {
let prevScrollPos = window.pageYOffset;
let isScrolling = false;
const minScrollDistance = window.innerWidth <= MOBILE_BREAKPOINT ? 10 : 20;
const fixedHeaderElement = document.querySelector('.fixed-header');
const navElement = parentElement.querySelector(
'[data-behavior="squad-navbar--nav-list"]'
);
const navLinks = navElement.querySelectorAll('a');
// get the first link and navto block
const firstNavLink = navLinks[0];
const firstNavToElement = document.getElementById(firstNavLink.dataset.navTo);
// get the last link and navto block
const lastNavLink = navLinks[navLinks.length - 1];
const lastNavToElement = document.getElementById(lastNavLink.dataset.navTo);
const onScroll = () => {
if (isScrolling) {
return;
}
isScrolling = true;
const startPoint =
firstNavToElement.offsetHeight + firstNavToElement.offsetTop;
const stopPoint =
lastNavToElement.offsetHeight + lastNavToElement.offsetTop;
setTimeout(() => {
const currentScrollPos = window.pageYOffset;
if (currentScrollPos > startPoint && currentScrollPos < stopPoint) {
parentElement.style.position = 'fixed';
// Scrolling down (at least 20px)
if (currentScrollPos > prevScrollPos + minScrollDistance) {
// position it to top
parentElement.style.top = 0;
// Scroll up (at least 20px)
} else if (currentScrollPos < prevScrollPos - minScrollDistance) {
// if header is visible then set its offsetHeight as offset top position of nav element
parentElement.style.top = `${fixedHeaderElement.offsetHeight - 1}px`;
}
} else {
parentElement.style.position = 'static';
}
prevScrollPos = currentScrollPos;
isScrolling = false;
}, 110);
};
return onScroll;
};
const bindEvents = parentElement => {
const onScroll = initFixedNavbar(parentElement);
const navlistElement = parentElement.querySelector(
'[data-behavior="squad-navbar--nav-list"]'
);
const navlistLinks = navlistElement.querySelectorAll('a');
navlistElement.addEventListener('click', event => {
event.preventDefault();
// switch off
window.removeEventListener('scroll', onScroll, { passive: true });
const { target } = event;
const navToElementId = target.dataset.navTo;
const navToElement = document.getElementById(navToElementId);
if (navToElement) {
navlistLinks.forEach(link => {
link.classList.remove('active');
});
target.classList.add('active');
navToElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// switch on
setTimeout(() => {
window.addEventListener('scroll', onScroll, { passive: true });
}, 50);
});
window.addEventListener('scroll', onScroll, { passive: true });
};
export default parentElement => {
// do scroll top before capturing scroll positions.
window.scroll({
top: 0,
left: 0,
behavior: 'smooth',
});
// event bindings
bindEvents(parentElement);
};
.squad-navbar {
display: flex;
justify-content: center;
width: 100%;
background-color: $white;
transition: all 0.4s ease-out 0s;
left: 0;
z-index: 9;
&__nav-list {
display: none;
}
@media screen and (min-width: $mq-small) {
&__nav-list {
display: flex;
width: 100%;
a {
@include text-s;
line-height: 4rem;
text-decoration: none;
text-transform: uppercase;
color: $color-interface-light;
border-bottom: 2px solid $light-blue;
width: 100%;
text-align: center;
&:not(:last-child) {
padding-right: 1rem;
}
&.active {
color: var(--brand-primary);
border-color: var(--brand-primary);
}
}
}
}
}
@media screen and (min-width: $mq-medium) {
.squad-navbar {
padding: 0 6rem;
}
}
<div class="squad-navbar" data-behaviour="squad-navbar">
<nav class="squad-navbar__nav-list" data-behavior="squad-navbar--nav-list" aria-label="Current player squad">
{{#each data}}
<a href="#" data-nav-to="{{id}}" {{#if additionalClasses}}class="{{additionalClasses}}" {{/if}}>
{{title}}
</a>
{{/each}}
</nav>
</div>