<div class="nav-in-page">
<span class="nav-in-page__anchor"></span>
<div class="nav-in-page__inner">
<div class="nav-in-page__content">
<a class="nav-in-page__link nav-in-page__trigger mobile">
<span class="link__label">Overview</span>
<div class="link__icon">
<svg class="icon-chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path fill="currentColor" d="M11.172 9.82L13 8 9.906 4.99 6.04 1.21c-.281-.28-.703-.28-.914 0l-.914.91c-.281.28-.281.7 0 .91L9.273 8l-5.062 4.97c-.281.28-.281.7 0 .91l.914.91c.281.28.703.28.914 0l5.133-4.97z" />
</svg>
</div>
</a>
<nav class="nav-in-page__nav">
<ul class="nav-in-page__items">
<li class="nav-in-page__item active">
<a href="#" class="nav-in-page__link">
<span class="link__label">Overview</span>
</a>
</li>
<li class="nav-in-page__item">
<a href="#" class="nav-in-page__link">
<span class="link__label">Videos</span>
</a>
</li>
<li class="nav-in-page__item">
<a href="#" class="nav-in-page__link">
<span class="link__label">Specifications</span>
</a>
</li>
<li class="nav-in-page__item">
<a href="#" class="nav-in-page__link">
<span class="link__label">Combine with</span>
</a>
</li>
<li class="nav-in-page__item">
<a href="#" class="nav-in-page__link">
<span class="link__label">Support</span>
</a>
</li>
</ul>
</nav>
</div>
<div class="nav-in-page__actions">
<div class="nav-in-page__action mobile">
<span>€ 1999</span>
<a href="#" class="link link--colored">
<span class="link__label">Buy now</span>
</a>
</div>
<div class="nav-in-page__action desktop">
<span>€ 1999</span>
<a href="#" target="_blank" class="action action--normal action--on-light">
<span class="action__label">Buy now</span>
<span class="action__bg"></span>
</a>
</div>
</div>
</div>
</div>
<div class="nav-in-page{{#if modifier }} nav-in-page--{{ modifier }}{{/if}}">
<span class="nav-in-page__anchor"></span>
<div class="nav-in-page__inner">
<div class="nav-in-page__content">
<a class="nav-in-page__link nav-in-page__trigger mobile">
<span class="link__label">{{ label }}</span>
<div class="link__icon">
{{render '@icon-chevron' }}
</div>
</a>
<nav class="nav-in-page__nav">
<ul class="nav-in-page__items">
{{#if items }}
{{#each items }}
<li class="nav-in-page__item{{#if active}} active{{/if}}">
<a href="{{ href }}" class="nav-in-page__link">
<span class="link__label">{{ label }}</span>
</a>
</li>
{{/each}}
{{/if}}
</ul>
</nav>
</div>
{{#if action}}
<div class="nav-in-page__actions">
<div class="nav-in-page__action mobile">
{{#if outofstock}}
<span>Out of stock</span>
{{else}}
<span>€ 1999</span>
{{/if}}
{{render '@link--colored' actionMobile merge=true }}
</div>
<div class="nav-in-page__action desktop">
{{#if outofstock}}
<span>Out of stock</span>
{{else}}
<span>€ 1999</span>
{{/if}}
{{render '@action' action merge=true }}
</div>
</div>
{{/if}}
</div>
</div>
modifier: false
label: Overview
items:
- href: '#'
label: Overview
active: true
- href: '#'
label: Videos
- href: '#'
label: Specifications
- href: '#'
label: Combine with
- href: '#'
label: Support
action:
label: Buy now
url: '#'
const NAVINPAGE = (function(window, undefined) {
const modes = {
mobile: 'mobile',
desktop: 'desktop',
};
const settings = {
debug: false,
mode: modes.mobile,
delay: 100,
observer: {
threshold: [0, .5, 1],
},
defaultLabel: '',
};
const selectors = {
wrapper: '.nav-in-page',
anchor: '.nav-in-page__anchor',
trigger: '.nav-in-page__trigger',
items: '.nav-in-page__items',
item: '.nav-in-page__item',
link: '.nav-in-page__link:not(.nav-in-page__trigger)',
linkLabel: '.link__label',
header: '.header',
hero: '.hero-product__actions',
target: '[data-nav-in-page]',
notSticky: '.nav-in-page--not-sticky',
};
const classes = {
open: 'nav--open',
isSticky: 'is-sticky',
active: 'active',
heroIsOutView: 'hero-is-out-view',
};
let resizeTimer = 0;
let canBeSticky = true;
const openIt = ($wrapper) => {
if (!$wrapper) { return; }
if (settings.debug) { console.log('NAV IN PAGE OPEN', $wrapper); }
$wrapper.classList.add(classes.open);
};
const closeIt = ($wrapper) => {
if (!$wrapper) { return; }
if (settings.debug) { console.log('NAV IN PAGE CLOSE', $wrapper); }
$wrapper.classList.remove(classes.open);
};
const toggle = ($wrapper) => {
if (!$wrapper) { return; }
if (settings.debug) { console.log('NAV IN PAGE TOGGLE', $wrapper); }
if ($wrapper.classList.contains(classes.open)) {
closeIt($wrapper);
} else {
openIt($wrapper);
}
};
const updateMode = () => {
const $wrapper = document.querySelector(selectors.wrapper);
if (!$wrapper) { return; }
let mode = window.getComputedStyle($wrapper, ':after').getPropertyValue('content');
if (mode) { mode = mode.replace(/"/g, ''); }
settings.mode = mode;
}
const onObserve = (entries) => {
const [entry] = entries;
if (!entry) { return; }
const { target: $target } = entry;
if (!$target) { return; }
const targetID = `#${$target.id}`;
const $link = document.querySelector(`${selectors.link}[href="${targetID}"]`);
const $wrapper = $link.closest(selectors.wrapper);
const $links = $wrapper.querySelectorAll(selectors.link);
const $trigger = $wrapper.querySelector(selectors.trigger);
const $triggerLabel = $trigger.querySelector(selectors.linkLabel);
if (settings.debug) { console.log('NAV IN PAGE OBSERVE', targetID, $link, $trigger, entry.isIntersecting); }
if (entry.isIntersecting) {
[...$links].forEach($link => {
if ($link.getAttribute('href') === targetID) {
$triggerLabel.innerText = $link.innerText;
$link.closest(selectors.item).classList.remove(classes.active);
$link.closest(selectors.item).classList.add(classes.active);
$trigger.classList.remove(classes.active);
} else {
$links[0].closest(selectors.item).classList.remove(classes.active);
}
});
} else {
$triggerLabel.innerText = settings.defaultLabel;
$link.closest(selectors.item).classList.remove(classes.active);
}
const $activeLinks = $wrapper.querySelectorAll(`${selectors.item}.${classes.active}`);
if (!$activeLinks.length) {
$links[0].closest(selectors.item).classList.add(classes.active);
}
}
const observeTarget = ($link) => {
const href = $link.getAttribute('href');
if (href.indexOf('#') !== 0 || href.length < 3) { return; }
// anchor link, find target
$target = document.querySelector(href);
if (!$target) { return; }
const observer = new window.IntersectionObserver(onObserve, settings.observer);
observer.observe($target);
}
const observeHero = () => {
const $wrapper = document.querySelector(selectors.wrapper);
const $hero = document.querySelector(selectors.hero);
if (!$hero) { return; }
const observer = new window.IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) {
$wrapper.classList.add(classes.heroIsOutView);
} else {
$wrapper.classList.remove(classes.heroIsOutView);
}
}, settings.observer);
observer.observe($hero);
};
const observeSelf = () => {
const $wrapper = document.querySelector(selectors.wrapper);
const $anchor = document.querySelector(selectors.anchor);
const observer = new window.IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) {
$wrapper.classList.add(classes.isSticky);
} else {
$wrapper.classList.remove(classes.isSticky);
}
}, settings.observer);
observer.observe($anchor);
};
const updateTop = () => {
const $wrapper = document.querySelector(selectors.wrapper);
const $header = document.querySelector(selectors.header);
if ($wrapper && canBeSticky && $header) {
$wrapper.style.top = `${$header.offsetHeight}px`;
}
}
const onResize = () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
const $wrappers = document.querySelectorAll(selectors.wrapper);
[...$wrappers].forEach(closeIt);
updateMode($wrappers[0]);
canBeSticky && updateTop();
}, 200);
}
const onTriggerClick = (e) => {
e.preventDefault();
if (settings.mode === modes.desktop) {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
return;
}
if (settings.debug) { console.log('NAV IN PAGE TRIGGER CLICK', e); }
const $trigger = e.currentTarget;
const $wrapper = $trigger.closest(selectors.wrapper);
toggle($wrapper);
};
const onLinkClick = (e) => {
const $link = e.currentTarget;
if (settings.debug) { console.log('NAV IN PAGE LINK CLICK', $link); }
const $wrapper = $link.closest(selectors.wrapper);
closeIt($wrapper);
};
const onDocumentClick = ({ target: $target }) => {
if (settings.mode === modes.desktop) { return; }
const $targetWrapper = $target.closest(selectors.wrapper);
if (!$targetWrapper) {
if (settings.debug) { console.log('NAV IN PAGE OUTSIDE CLICK', $targetWrapper); }
const $wrappers = document.querySelectorAll(selectors.wrapper, settings.observer);
[...$wrappers].forEach(closeIt);
}
}
const addItem = (href, label) => {
const $items = document.querySelector(selectors.items);
if (!$items || !href || !label) { return; }
const $item = document.createElement('li');
const html = `
<a href="#${href}" class="nav-in-page__link">
<span class="link__label">${label}</span>
</a>
`;
$item.classList.add('nav-in-page__item');
$item.innerHTML = html;
$items.appendChild($item);
};
const init = () => {
const $wrapper = document.querySelector(selectors.wrapper);
if (!$wrapper) { return; }
if (settings.debug) { console.log('NAV IN PAGE INIT'); }
canBeSticky = !document.querySelector(selectors.notSticky);
const $trigger = document.querySelector(selectors.trigger);
$trigger && $trigger.addEventListener('click', onTriggerClick);
settings.defaultLabel = $trigger.innerText || '';
const $links = document.querySelectorAll(selectors.link);
[...$links].forEach($link => {
$link.addEventListener('click', onLinkClick);
if ($link.getAttribute('href').indexOf('#') !== 0) { return; }
observeTarget($link);
});
observeHero();
observeSelf();
const $items = document.querySelectorAll(selectors.item);
[...$items].forEach(($item, i) => $item.style.transitionDelay = `${(i + 1) * settings.delay}ms`);
const $targets = document.querySelectorAll(selectors.target);
const targetArray = [...$targets];
targetArray.sort((a, b) => {
if (a.id > b.id) {
return 1;
}
if (a.id < b.id) {
return -1;
}
return 0;
});
targetArray.forEach(($target) => {
if (!$target.dataset.navInPage) { console.error('Label for page-in-nav is missing!'); return; }
if (!$target.id) { console.error(`ID on ${$target} for page-in-nav is missing!`); return; }
addItem($target.id, $target.dataset.navInPage);
});
document.addEventListener('click', onDocumentClick);
updateMode($wrapper);
canBeSticky && updateTop();
};
document.addEventListener('DOMContentLoaded', init);
window.addEventListener('resize', onResize);
} (window));
.nav-in-page {
background: var(--color--neutrals-0);
padding: 0 var(--gutter--bucket);
position: sticky;
top: 0;
z-index: 998;
&.is-sticky {
border-bottom: var(--divider);
}
&.nav-in-page--not-sticky,
.nav-in-page--not-sticky & {
position: relative;
border: none;
}
&.nav-in-page--background,
.nav-in-page--background & {
background: var(--color--neutrals-1);
}
}
.nav-in-page:after {
content: 'mobile';
display: none;
}
.nav-in-page:before {
background: rgba(0,0,0,.2);
content: '';
display: block;
height: 0;
left: 0;
opacity: 0;
position: absolute;
top: 100%;
transition: opacity 300ms;
width: 100%;
}
.nav--open:before {
height: 100vh;
opacity: 1;
}
.nav-in-page__anchor {
transform: translateY(calc((var(--header-height) * -1) - 3px));
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 1px;
}
.nav-in-page__inner {
align-items: center;
background: var(--color--neutrals-0);
display: flex;
flex-flow: row nowrap;
height: 50px;
justify-content: space-between;
position: relative;
z-index: 10;
flex: 1 1 auto;
margin-right: auto;
margin-left: auto;
max-width: 1140px;
width: 100%;
.nav-in-page--not-sticky & {
border-bottom: var(--divider);
}
.nav-in-page--background & {
background: var(--color--neutrals-1);
}
}
.nav-in-page__content {
display: flex;
flex-flow: column;
}
.nav-in-page__nav {
background: var(--color--neutrals-0);
left: calc(var(--gutter--bucket) * -1);
right: calc(var(--gutter--bucket) * -1);
opacity: 0;
padding-left: var(--gap--3);
pointer-events: none;
position: absolute;
transition: opacity 300ms;
top: 100%;
.nav-in-page--background & {
background: var(--color--neutrals-1);
}
.nav--open & {
opacity: 1;
pointer-events: initial;
}
}
.nav-in-page__items {
font-size: var(--p--2);
margin: 0 !important;
padding: var(--gap--3) 0 0 0;
list-style: none;
}
.nav-in-page__item {
border-bottom: var(--divider);
opacity: 0;
padding: var(--gap--3) var(--gap--3) var(--gap--3) 0;
transition: all 300ms;
&:last-child {
border-bottom: none;
}
.nav--open & {
opacity: 1;
padding-left: var(--gap--3);
&.active {
display: none;
}
}
}
.nav-in-page__link {
color: inherit;
padding: var(--gap--1) 0;
transition: opacity 300ms;
opacity: .6;
text-decoration: none;
&:active, &:focus, &:hover, .active & {
color: inherit;
opacity: 1;
}
.nav-in-page--not-sticky & {
opacity: 1;
}
}
.nav-in-page__trigger {
align-items: center;
cursor: pointer;
display: flex;
flex-flow: row nowrap;
font-size: var(--h--6);
font-weight: bold;
.link__icon {
display: inline-flex;
color: var(--color--action);
height: var(--p--2);
margin-left: var(--gap--2);
transition: transform 300ms;
transform: rotate(90deg);
width: var(--p--2);
.nav--open & {
transform: rotate(-90deg);
}
}
&:active, &:focus, &:hover {
opacity: 1;
}
}
.nav-in-page__actions {
transition: opacity 250ms;
opacity: 0;
pointer-events: none;
flex-shrink: 0;
.hero-is-out-view & {
opacity: 1;
pointer-events: all;
}
}
.nav-in-page__action {
display: flex;
align-items: center;
& > * {
flex: 0 1 auto;
margin-left: var(--gap--4);
&:first-child {
margin-left: 0;
}
}
& > span {
flex: 0 0 auto;
}
}
@media screen and (min-width: 768px) {
.nav-in-page:after {
content: 'desktop';
}
.nav-in-page__inner {
height: 70px;
}
.nav-in-page__trigger {
font: inherit;
.link__icon {
display: none;
}
}
.nav-in-page__content {
flex-flow: row wrap;
width: 100%;
}
.nav-in-page__nav {
display: flex;
opacity: 1;
padding-left: 0;
position: static;
pointer-events: initial;
top: 0;
width: auto;
background-color: transparent;
}
.nav-in-page__items {
display: flex;
flex-flow: row wrap;
font-size: inherit;
padding: 0;
}
.nav-in-page__item {
border-bottom: none;
display: flex !important;
opacity: 1;
padding-left: var(--gap--12);
padding-right: 0;
padding-bottom: 0;
padding-top: 0;
width: auto;
&:first-child {
padding-left: 0;
}
}
.nav-in-page__link {
display: flex;
position: relative;
align-items: center;
height: 70px;
padding: 0;
&:active, &:focus, &:hover, .active & {
opacity: 1;
}
&:after {
transition: height 250ms;
display: block;
height: 0;
content: '';
left: 0;
position: absolute;
bottom: 0;
width: 100%;
background: var(--color--action);
}
&:hover, .active & {
&:after {
height: 6px;
}
}
}
}
No notes defined.