Animate menu item with anime.js when scrolling down to section - javascript

I am trying to combine code from #David, with code from #Luisdanielroviracontreras to make a cool index menu for my portfolio case studies. So far I have been able to animate the item background when clicked:
https://codepen.io/andyradall/pen/BaKJpbM
But as you can see, I run into problems when trying to animate the background of the menu item based on scroll position. The background item ends up in the wrong place: https://codepen.io/andyradall/pen/WNwdRLq
Note. My problem:
specificity and selecting the right HTML elements in the js.
Attaching the active class to the button rather than just the a
Alternatively I need to remove the button and attach the .effect to the a
Here is the current code for my hightlight navigation section:
// Header Sticky
window.onscroll = function() {myFunction()};
var header = document.getElementById("navigation");
var sticky = header.offsetTop;
function myFunction() {
if (window.pageYOffset > sticky) {
header.classList.add("sticky");
} else {
header.classList.remove("sticky");
}
}
/* Header buttons Luis that attatches active to buttons on click. */
/*
const buttons = document.querySelectorAll('.header-navbar button')
const effect = document.querySelector('.effect')
buttons.forEach((item) => {
item.addEventListener('click', (evt) => {
const x = evt.target.offsetLeft
buttons.forEach((btn) => { btn.classList.remove('active') })
evt.target.classList.add('active')
anime({
targets: '.effect',
left: `${x}px`,
duration: 600,
})
})
})
*/
/* header buttons on scroll from #Daniel at stackoverflow that misses the buttons and only attatches the active to the link*/
// caches the navigation links
var $navigationLinks = $('#navigation > ul > li > a');
// what I really want:
/*var buttons = document.querySelectorAll('.header-navbar button')
var effect = document.querySelector('.effect')*/
// cache (in reversed order) the sections
var $sections = $($(".section").get().reverse());
// map each section id to their corresponding navigation link
// here i really want to map each section id to the corresponding button
var sectionIdTonavigationLink = {};
$sections.each(function() {
var id = $(this).attr('id');
sectionIdTonavigationLink[id] = $('#navigation > ul > li > a[href=\\#' + id + ']');
});
// throttle function, enforces a minimum time interval
function throttle(fn, interval) {
var lastCall, timeoutId;
return function () {
var now = new Date().getTime();
if (lastCall && now < (lastCall + interval) ) {
// if we are inside the interval we wait
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
lastCall = now;
fn.call();
}, interval - (now - lastCall) );
} else {
// otherwise, we directly call the function
lastCall = now;
fn.call();
}
};
}
function highlightNavigation() {
// get the current vertical position of the scroll bar
var scrollPosition = $(window).scrollTop();
const effect = document.querySelector('.effect')
// iterate the sections
$sections.each(function() {
var currentSection = $(this);
// get the position of the section
var sectionTop = currentSection.offset().top;
var x = currentSection.offset().top;
console.log(x);
// if the user has scrolled over the top of the section
if (scrollPosition >= sectionTop) {
// get the section id
var id = currentSection.attr('id');
// get the corresponding navigation link
var $navigationLink = sectionIdTonavigationLink[id];
// if the link is not active
if ($navigationLink.hasClass('active')) {
// remove .active class from all the links
$navigationLinks.removeClass('active');
// add .active class to the current link
$navigationLink.addClass('active')
anime({
targets: '.effect',
left: `${x}px`,
duration: 600,
});
}
// we have found our section, so we return false to exit the each loop
return false;
}
});
}
$(window).scroll( throttle(highlightNavigation,100) );
// if you don't want to throttle the function use this instead:
//$(window).scroll( highlightNavigation );
/* Global */
* {
list-style: none;
outline: none;
padding: 0;
margin: 0;
font-family: 'Poppins', sans-serif;
box-sizing: border-box;
}
body {
--primary: 25,91,255;
--color: 44, 62, 80;
display: flex;
align-items: flex-start;
justify-content: center;
background: #f4f7f8;
/* height: calc(var(--vh, 1vh) * 100); */
/* overflow: hidden; */
color: rgb(var(--color));
width: 100%;
}
main {
width: 100%;
}
/* .sticky attatch to #navigation on scroll past #navigation */
.sticky {
position: fixed;
top: 0;
}
.header-navbar ul {
list-style: none;
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
background: #fff;
}
.header-navbar {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
background: #fff;
padding: 24px 8px 24px 8px;
box-shadow: 0px 0px 30px 0px rgba(0,0,0,.05);
}
.header-navbar button {
width: 140px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
border: 0px;
background: transparent;
border-radius: 20px;
transition: all .25s ease;
}
/*.header-navbar a {
width: 160px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 0px;
background: transparent;
border-radius: 20px;
transition: all .25s ease;
}*/
.header-navbar a{
/*color: #333; */
font-size: 1rem;
padding: 10px 10px 10px 10px;
text-decoration: none;
}
/* to use if adding back link*/
.header-navbar a:active:not(.float) {
transform: scale(1.2);
}
.header-navbar button.active {
color: rgb(232, 76, 79);
}
/* if icon not text */
.header-navbar button i {
font-size: 1.2rem;
pointer-events: none;
}
/*background effect*/
.con-effect {
position: absolute;
width: 100%;
height: 100%;
/*top: 0px;
left: 0px;*/
overflow: hidden;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
}
.effect {
background: rgba(232, 76, 232, 0.15);
width: 160px;
height: 70px;
position: absolute;
left: 100px;
border-radius: 20px;
}
/* Content sections styling */
.hero {
width:100%;
padding: 32px;
}
.section{
width:100%;
height: 500px;
padding: 32px;
}
#first {
background-color: #8face0;
}
#second {
background-color: #a388e8;
}
#third {
background-color: #f4769e;
}
#fourth {
background-color: #8face0;
}
#fifth {
background-color: #a388e8;
}
#bottom-spacer {
height: 2200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.0/anime.min.js></script>
<body>
<main>
<section id="intro" class="hero">
<div>
<h1>This will be the hero section</h1>
</div>
</section>
<div id="navigation" class="header-navbar">
<div class="con-effect">
<div class="effect"></div>
</div>
<ul>
<li><button>Introduction</button></li>
<li><button>1. User research</button></li>
<li><button>2. Ideation</button></li>
<li><button>3. Design phase</button></li>
<li><button>4. Testing</button></li>
<li><button>5. Results</button></li>
</ul>
</div>
</div>
<section class="sections">
<section id="first" class="section">
<h2>1. User research</h2>
</section>
<section id="second" class="section">
<h2>2. Ideate</h2>
</section>
<section id="third" class="section">
<h2>3. Design phase</h2>
</section>
<section id="fourth" class="section">
<h2>4. Testing</h2>
</section>
<section id="fifth" class="section">
<h2>5. Results</h2>
</section>
<section id="bottom-spacer">
</section>
</section>
</main>
</body>

Finally solve it!
Solution:
Find the active navigationlink offset left, store it in var x
Use navigationlink offset left value (x) to place the background effect
Now, there is only styling and finetuning left :)
Final will receive updates here: https://codepen.io/andyradall/pen/WNwdRLq
// sticky header
window.onscroll = function() {myFunction()};
var header = document.getElementById("navigation");
//var effectClass = document.getElementById("effectClass")
var sticky = header.offsetTop;
function myFunction() {
if (window.pageYOffset > sticky) {
header.classList.add("sticky");
//effectClass.classList.add("effect")
} else {
header.classList.remove("sticky");
//effectClass.classList.remove("effect")
}
}
// cache the navigation links
var $navigationLinks = $('#navigation > ul > li > a');
// cache (in reversed order) the sections
var $sections = $($(".section").get().reverse());
// map each section id to their corresponding navigation link
var sectionIdTonavigationLink = {};
$sections.each(function() {
var id = $(this).attr('id');
sectionIdTonavigationLink[id] = $('#navigation > ul > li > a[href=\\#' + id + ']');
});
// throttle function, enforces a minimum time interval
function throttle(fn, interval) {
var lastCall, timeoutId;
return function () {
var now = new Date().getTime();
if (lastCall && now < (lastCall + interval) ) {
// if we are inside the interval we wait
clearTimeout(timeoutId);
timeoutId = setTimeout(function () {
lastCall = now;
fn.call();
}, interval - (now - lastCall) );
} else {
// otherwise, we directly call the function
lastCall = now;
fn.call();
}
};
}
function highlightNavigation() {
// get the current vertical position of the scroll bar
var scrollPosition = $(window).scrollTop();
const effect = document.querySelector('.effect')
// iterate the sections
$sections.each(function() {
var currentSection = $(this);
// get the position of the section
var sectionTop = currentSection.offset().top;
// if the user has scrolled over the top of the section
if (scrollPosition >= sectionTop) {
// get the section id
var id = currentSection.attr('id');
// get the corresponding navigation link
var $navigationLink = sectionIdTonavigationLink[id];
// if the link is not active
if (!$navigationLink.hasClass('active')) {
// remove .active class from all the links
$navigationLinks.removeClass('active');
// add .active class to the current link
$navigationLink.addClass('active');
var x = $navigationLink.offset().left;
anime({
targets: '.effect',
left: `${x-28}px`,
duration: 600,
endDelay: 1000,
})
}
// we have found our section, so we return false to exit the each loop
return false;
}
});
}
$(window).scroll( throttle(highlightNavigation,100) );
// if you don't want to throttle the function use this instead:
// $(window).scroll( highlightNavigation );
* {
list-style: none;
outline: none;
padding: 0;
margin: 0;
font-family: 'Poppins', sans-serif;
box-sizing: border-box;
}
body {
--primary: 25,91,255;
--color: 44, 62, 80;
display: flex;
align-items: flex-start;
justify-content: center;
background: #f4f7f8;
/* height: calc(var(--vh, 1vh) * 100); */
/* overflow: hidden; */
color: rgb(var(--color));
width: 100%;
}
main {
width: 100%;
}
/* .sticky attatch to #navigation on scroll past #navigation */
.sticky {
position: fixed;
top: 0;
}
.header-navbar ul {
list-style: none;
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
background: #fafafa;
}
.header-navbar {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
background: #fafafa;
padding: 2px 0 2px 0;
box-shadow: 0 1px 2px rgba(0,0,0,0.06),
0 2px 4px rgba(0,0,0,0.06),
0 4px 8px rgba(0,0,0,0.06),
0 8px 16px rgba(0,0,0,0.06),
0 16px 32px rgba(0,0,0,0.06),
0 32px 64px rgba(0,0,0,0.06);
}
.header-navbar li {
width: 160px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 0px;
background: transparent;
border-radius: 20px;
transition: all .25s ease;
}
/*.header-navbar a {
width: 160px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 0px;
background: transparent;
border-radius: 20px;
transition: all .25s ease;
}*/
.header-navbar a{
color: #333;
font-size: 1rem;
/*padding: 8px 8px 8px 8px;*/
text-decoration: none;
}
/* :not excludes class for specific use */
.header-navbar a:active:not(.float) {
transform: scale(1.2);
}
.active {
color: rgb(232, 76, 79)!important;
}
.header-navbar a:active {
color: rgb(232, 76, 79)!important;
}
/* if icon not text */
.header-navbar a i {
font-size: 1.2rem;
pointer-events: none;
}
/*background effect*/
.con-effect {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
overflow: hidden;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
}
.effect {
background: rgba(232, 76, 232, 0.15);
width: 178px;
height: 40px;
position: absolute;
left: 1px;
border-radius: 40px;
}
/* Content sections styling */
.hero {
width:100%;
padding: 32px;
}
.section{
color: #666;
width:100%;
/*height: 500px;*/
padding: 32px;
}
#first {
background-color: #8face0;
}
#second {
background-color: #a388e8;
}
#third {
background-color: #f4769e;
}
#fourth {
background-color: #8face0;
}
#fifth {
background-color: #a388e8;
}
#bottom-spacer {
height: 2200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.0/anime.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<main>
<section id="intro" class="hero section">
<div>
<h1>This will be the hero section</h1>
</div>
</section>
<div id="navigation" class="header-navbar">
<div class="con-effect">
<div id="effectClass" class="effect"></div>
</div>
<ul>
<li>Introduction</li>
<li>1. User research</li>
<li>2. Ideation</li>
<li>3. Design phase</li>
<li>4. Testing</li>
<li>5. Results</li>
</ul>
</div>
<section class="sections">
<section id="first" class="section">
<h2>1. User research</h2>
</section>
<section id="second" class="section">
<h2>2. Ideate</h2>
</section>
<section id="third" class="section">
<h2>3. Design phase</h2>
</section>
<section id="fourth" class="section">
<h2>4. Testing</h2>
</section>
<section id="fifth" class="section">
<h2>5. Results</h2>
</section>
<section id="bottom-spacer">
</section>
</section>
</main>
</body>

Related

Carousel slides appear beneath each other on click

I'm using HTML/CSS and vanilla JS. I'm trying to create a simple carousel. However, whenever I click the 'next' button, the following slides show up under the last one. I'm not sure why this is happening. Code snippets are below. Can someone explain why this is happening?
By the way, I have not optimized this page for media queries yet, so it might look a little weird on smaller screens.
const buttons = document.querySelectorAll("[data-carousel-btn]");
buttons.forEach((button) => {
button.addEventListener("click", () => {
const offset = button.dataset.carouselBtn === "next" ? 1 : -1;
const slidesContainer = button
.closest("[data-carousel]")
.querySelector("[data-carousel-slides");
const slides = slidesContainer.querySelectorAll("[data-carousel-slide]");
const activeSlide = slidesContainer.querySelector("[data-active]");
const activeSlideIndex = [...slides].indexOf(activeSlide);
const nextSlideIndex = activeSlideIndex + offset;
if (nextSlideIndex < 0) {
slides[slides.length + nextSlideIndex].dataset.active = true;
return delete activeSlide.dataset.active;
}
if (nextSlideIndex >= slides.length) {
slides[0].dataset.active = true;
return delete activeSlide.dataset.active;
}
slides[nextSlideIndex].dataset.active = true;
return delete activeSlide.dataset.active;
});
});
.carouselContainer {
width: 1000px;
height: 500px;
position: relative;
display: flex;
justify-content: center;
border-style: dashed;
border-color: #010043;
}
.carouselContainer>ul {
padding: 0;
margin: 0;
list-style: none;
}
.slide {
display: flex;
width: 400px;
height: 500px;
justify-content: center;
align-items: center;
inset: 0;
opacity: 0;
transition-property: opacity;
transition-duration: 200ms;
transition-timing-function: ease-in-out;
transition-delay: 200ms;
border-style: dashed;
border-color: var(--magenta6);
}
.slide[data-active] {
opacity: 1;
z-index: 1;
transition-delay: 0ms;
}
.slideContent {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
width: 300px;
height: 425px;
border-style: dashed;
border-color: #010043;
}
.slideContent .slideImg {
margin: 0;
width: 200px;
height: 200px;
border-radius: 100px;
z-index: 10px;
}
.slideContent .slideTxt {
border: magenta;
border-style: dashed;
height: 500px;
}
.carousel-button {
position: absolute;
background: none;
border: none;
outline: none;
font-size: 4rem;
top: 50%;
transform: translateY(-50%);
z-index: 2;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
padding: 0 0.5rem;
border-radius: 0.25rem;
background-color: rgba(0, 0, 0, 0.1);
transition: 0.5s;
}
.carousel-button:hover,
.carousel-button:focus {
background-color: rgba(0, 0, 0, 0.3);
color: #fff;
}
.carousel-button[data-carousel-btn="prev"] {
left: 1rem;
}
.carousel-button[data-carousel-btn="next"] {
right: 1rem;
}
<section class="row4 animateOnScroll" style="margin: 0 100px 100px 100px;">
<h2>don't just take it from us!</h2>
<div class="carouselContainer" data-carousel>
<button class="carousel-button" data-carousel-btn="prev">
❮
</button>
<button class="carousel-button" data-carousel-btn="next">
❯
</button>
<div class="carouselSlides">
<ul data-carousel-slides>
<li class="slide" data-carousel-slide data-active>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</li>
<li class="slide" data-carousel-slide>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</li>
<li class="slide" data-carousel-slide>
<div class="slideContent">
<div>
<img class="slideImg" src="./assets/imgPlaceholder.jpg">
</div>
<div class="slideTxt">
<div class="slideDesc">
<p style="color:black;">hello</p>
</div>
</div>
</div>
</ul>
</div>
</div>
</section>
part of your problem is your ul element is unstyled, its child li elements are going to stack as they normally do on top of each other. giving the ul element display: flex; will put your li's side by side.
If I were you, I would review each nested element of my tree and figure out its purpose, then remove it if not necessary. for example, div.carouselSlides does not seem like its serving any purpose that the ul could not do itself, at least in this small example.
Also, looking at an established project for implementation ideas (or just using it) might be a good idea . https://swiperjs.com/ is very established with powerful config options
Basically there should be 2 containers:
The element that is the parent of the <button>s and the "frame" that holds each slide. In the example it is article.box.
The element that is the parent of each div.slide in the example is section.frame.
Each container should be position: relative and all children of said containers should be position: absolute. Doing so will:
provide precision positioning of the <button>s and section.frame within the perimeters of article.box
allow all div.slide to hide underneath the visible layers (z-index:0+) with z-index: -1 and be visible with .active class at z-index: 1.
The JavaScript is optional, it's just written better but function should be basically the same.
View in full page mode, the image placeholder service does not have dynamic images.
const data = [
{img:"https://placem.at/people?random=1", cap:"People 1"},
{img:"https://placem.at/places?random=1", cap:"Places 2"},
{img:"https://placem.at/things?random=1", cap:"Things 3"}
];
const slides = genSlides(data);
const box = document.querySelector('.box')
const buttons = box.querySelectorAll("button");
buttons.forEach((button) => {
button.addEventListener("click", function(event) {
const offset = button.classList.contains("next") ? 1 : -1;
const active = box.querySelector(".active");
const actIdx = slides.indexOf(active);
const nextIdx = actIdx + offset;
active.classList.remove("active");
if (nextIdx < 0) {
return slides[slides.length + nextIdx].classList.add("active");
}
if (nextIdx >= slides.length) {
return slides[0].classList.add("active");
}
return slides[nextIdx].classList.add("active");
});
});
function genSlides(array) {
const frame = document.querySelector(".frame");
array.forEach(slide => {
frame.insertAdjacentHTML("beforeend", `
<div class="slide">
<figure>
<img src="${slide.img}" width="480">
<figcaption class="cap">${slide.cap}</figcaption>
</figure>
</div>`);
});
const slides = Array.from(frame.querySelectorAll(".slide"));
slides[0].classList.add("active");
return slides;
}
.box {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 96vw;
min-height: 96vh;
border: 3px dashed #000;
}
.frame {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
/* The element that is the parent of all .slide should be relative so the
slides, which are absolute positioned, can sit within .frame's perimeter */
position: relative;
}
.slide {
display: flex;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
/* All slides should be out of normal flow */
position: absolute;
/* All slides should be in the layer "under" the visible layer (z-index: 0+) */
z-index: -1;
opacity: 0;
animation: opacity 0.7s ease-in;
}
.active {
opacity: 1;
z-index: 1;
}
figure {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
max-height: 100%;
margin: 0;
padding: 0;
border-style: 1px dashed #000;
}
img {
object-fit: contain;
}
.cap {
min-width: 100%;
text-align: center;
white-space: pre;
}
button {
display: inline-flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
z-index: 2;
padding: 0 0.5rem;
border: none;
border-radius: 0.25rem;
outline: none;
font-size: 4rem;
color: rgba(255, 255, 255, 0.5);
background: none;
background-color: rgba(0, 0, 0, 0.1);
transform: translateY(-50%);
transition: 0.5s;
cursor: pointer;
}
button:hover,
button:focus {
color: #fff;
background-color: rgba(0, 0, 0, 0.3);
}
button:active {
color: rgba(0, 0, 0, 0.5);
background: none;
}
.prev {
left: 1rem;
}
.next {
right: 1rem;
}
<main>
<h2>Content Title</h2>
<article class="box">
<button class="prev">❮</button>
<button class="next">❯</button>
<section class="frame"></section>
</article>
</main>

Removing onScroll event when menu is clicked

When menu is clicked I want it to be fixed not scrollable.
How can I remove the event listener on scroll so the menu is staying fixed and scrolling up and down? JavaScript is not my strong skill and if someone with more experience can help me out, it would be greatly appreciated. Thank you
const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;
triggerMenu.addEventListener("click", () => {
header.classList.toggle("menu-open");
});
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
header.classList.remove(scrollUp);
return;
}
if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
// down
header.classList.remove(scrollUp);
header.classList.add(scrollDown);
} else if (
currentScroll < lastScroll &&
header.classList.contains(scrollDown)
) {
// up
header.classList.remove(scrollDown);
header.classList.add(scrollUp);
}
lastScroll = currentScroll;
});
:root {
--white: #fff;
--black: #221f1f;
--lightpurple: #9e91f2;
--darkgray: #1e1f26;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
button {
background: transparent;
border: none;
cursor: pointer;
outline: none;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
body {
position: relative;
font: 16px/1.5 sans-serif;
color: var(--white);
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
}
/* MAIN RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.trigger-menu-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: end;
padding: 20px;
z-index: 2;
background: var(--lightpurple);
transition: transform 0.4s;
}
.page-header .trigger-menu {
display: flex;
align-items: center;
font-size: 1.3rem;
color: var(--white);
letter-spacing: 0.2em;
}
.page-header .trigger-menu svg {
fill: var(--white);
margin-right: 8px;
transition: transform 0.3s;
}
.page-header .menu {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: none;
text-align: center;
padding: 15vh 0 5vh;
overflow: auto;
z-index: 1;
background: var(--lightpurple);
}
.page-header .menu a {
font-size: 3rem;
}
.page-header .sub-menu a {
font-size: 1.5rem;
}
.lottie-wrapper {
position: fixed;
bottom: 50px;
right: 25px;
z-index: 1;
padding: 5px;
border-radius: 5px;
}
.page-main section {
position: relative;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
height: 100vh;
}
.page-main section::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.25);
}
/* BODY CLASSES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.menu-open {
overflow: hidden;
}
.menu-open .trigger-menu-wrapper {
background: transparent;
position: fixed;
}
.menu-open .page-header .menu {
display: block;
}
.menu-open .page-header svg {
transform: rotate(45deg);
}
.menu-open-with-lottie .page-header .menu {
padding: 5vh 0;
}
.scroll-down .trigger-menu-wrapper {
transform: translate3d(0, -100%, 0);
}
.scroll-down .lottie-wrapper {
background: var(--darkgray);
}
.scroll-up .trigger-menu-wrapper {
transform: none;
}
.scroll-up:not(.menu-open) .trigger-menu-wrapper {
background: var(--lightpurple);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.35);
}
/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
position: fixed;
right: 25px;
bottom: 10px;
display: flex;
align-items: center;
font-size: 1rem;
padding: 5px;
border-radius: 5px;
background: var(--darkgray);
}
.page-footer a {
display: flex;
margin-left: 4px;
}
<script src="https://unpkg.com/#lottiefiles/lottie-player#latest/dist/lottie-player.js"></script>
<header class="hiding-header">
<nav class="page-header">
<div class="trigger-menu-wrapper">
<button class="trigger-menu">
<svg width="12" height="12" viewBox="0 0 24 24">
<path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z" />
</svg>
<span>MENU</span>
</button>
</div>
<ul class="menu">
<li>
About
<ul class="sub-menu">
<li>
History
</li>
<li>
President
</li>
<li>
Team
</li>
<li>
Process
</li>
<li>
Clients
</li>
</ul>
</li>
</ul>
</nav>
</header>
<!-- <a href="" role="button" aria-label="Toggle menu" class="lottie-wrapper">
<lottie-player src="https://assets10.lottiefiles.com/datafiles/9gIwZ2uiiKglyb0/data.json" style="width: 60px; height: 60px;"></lottie-player>
</a> -->
<main class="page-main">
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/freedom.jpg);"></section>
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/travel.jpg);"></section>
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/holidays.jpg);"></section>
</main>
<footer class="page-footer">
<!-- <span>made by </span>
<a href="https://georgemartsoukos.com/" target="_blank">
<img width="24" height="24" src="https://assets.codepen.io/162656/george-martsoukos-small-logo.svg" alt="George Martsoukos logo">
</a> -->
</footer>
You can use
removeEventListener(type, listener);
although you may need to make your scroll event a function rather than an anonymous function
const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;
triggerMenu.addEventListener("click", () => {
header.classList.toggle("menu-open");
window.removeEventListener("scroll", handleScroll)
});
window.addEventListener("scroll", handleScroll);
function handleScroll() {
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
header.classList.remove(scrollUp);
return;
}
if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
// down
header.classList.remove(scrollUp);
header.classList.add(scrollDown);
} else if (
currentScroll < lastScroll &&
header.classList.contains(scrollDown)
) {
// up
header.classList.remove(scrollDown);
header.classList.add(scrollUp);
}
lastScroll = currentScroll;
}
To add to #jay's point you cannot call removeEventListener without the original listener function. If you call addEventListener with an anonymous function, you cannot easily remove it, note you could forcibly remove all listeners for DOM elements as mentioned here but it's ugly and not ideal.
Good ✅
const handleScroll = () => {
console.log('scrolling...');
};
document.addEventListener('scroll', handleScroll);
document.removeEventListener('scroll', handleScroll);
Bad ❌
document.addEventListener('scroll', () => {
console.log('scrolling...');
})
document.removeEventListener('scroll', () => {
console.log('scrolling...');
}) // does not throw error but fails to remove listener
Note: It can be a little deceiving calling removeEventListener without getting an error thinking it worked but this is not the case. Removal is attempted but not certain if no matching listeners are found.
More considerations
Using the original listener is one part put the removal of listeners depends on the options as well, see docs for more details.
Dev Tools helper
You can always use the dev tools Event Listeners tab to inspect an element or document.body and see all active listeners to know for sure when listeners are removed.
Solution to disable scroll when menu open
Yeah your question was not really about the scroll listener but how to disable the scroll, as you have found out the listener does not stop the scroll just stops listening.
For what you are looking for there are few options the vary in complexity, but the easiest fastest way to make it work is just to add a class to turn scrolling off on the scrolling element.
In you case you are scrolling the document.body, so we need to add a class to the body when the menu is clicked and remove it when it's clicked again to close.
triggerMenu.addEventListener("click", () => {
header.classList.toggle("menu-open");
document.body.classList.toggle('disable-scroll'); // <-- add this line
});
body.disable-scroll {
overflow: hidden; // prevents scrolling on the body
}
See working demo below...
const header = document.querySelector(".hiding-header");
const triggerMenu = document.querySelector(".page-header .trigger-menu");
const nav = document.querySelector(".page-header nav");
const menu = document.querySelector(".page-header .menu");
const scrollUp = "scroll-up";
const scrollDown = "scroll-down";
let lastScroll = 0;
triggerMenu.addEventListener("click", () => {
header.classList.toggle("menu-open");
document.body.classList.toggle('disable-scroll');
});
window.addEventListener("scroll", handleScroll);
function handleScroll(e) {
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
header.classList.remove(scrollUp);
return;
}
if (currentScroll > lastScroll && !header.classList.contains(scrollDown)) {
// down
header.classList.remove(scrollUp);
header.classList.add(scrollDown);
} else if (
currentScroll < lastScroll &&
header.classList.contains(scrollDown)
) {
// up
header.classList.remove(scrollDown);
header.classList.add(scrollUp);
}
lastScroll = currentScroll;
}
:root {
--white: #fff;
--black: #221f1f;
--lightpurple: #9e91f2;
--darkgray: #1e1f26;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
button {
background: transparent;
border: none;
cursor: pointer;
outline: none;
}
ul {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
body {
position: relative;
font: 16px/1.5 sans-serif;
color: var(--white);
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
body.disable-scroll {
overflow: hidden;
}
/* MAIN RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.trigger-menu-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: end;
padding: 20px;
z-index: 2;
background: var(--lightpurple);
transition: transform 0.4s;
}
.page-header .trigger-menu {
display: flex;
align-items: center;
font-size: 1.3rem;
color: var(--white);
letter-spacing: 0.2em;
}
.page-header .trigger-menu svg {
fill: var(--white);
margin-right: 8px;
transition: transform 0.3s;
}
.page-header .menu {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: none;
text-align: center;
padding: 15vh 0 5vh;
overflow: auto;
z-index: 1;
background: var(--lightpurple);
}
.page-header .menu a {
font-size: 3rem;
}
.page-header .sub-menu a {
font-size: 1.5rem;
}
.lottie-wrapper {
position: fixed;
bottom: 50px;
right: 25px;
z-index: 1;
padding: 5px;
border-radius: 5px;
}
.page-main section {
position: relative;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
height: 100vh;
}
.page-main section::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.25);
}
/* BODY CLASSES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.menu-open {
overflow: hidden;
}
.menu-open .trigger-menu-wrapper {
background: transparent;
position: fixed;
}
.menu-open .page-header .menu {
display: block;
}
.menu-open .page-header svg {
transform: rotate(45deg);
}
.menu-open-with-lottie .page-header .menu {
padding: 5vh 0;
}
.scroll-down .trigger-menu-wrapper {
transform: translate3d(0, -100%, 0);
}
.scroll-down .lottie-wrapper {
background: var(--darkgray);
}
.scroll-up .trigger-menu-wrapper {
transform: none;
}
.scroll-up:not(.menu-open) .trigger-menu-wrapper {
background: var(--lightpurple);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.35);
}
/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.page-footer {
position: fixed;
right: 25px;
bottom: 10px;
display: flex;
align-items: center;
font-size: 1rem;
padding: 5px;
border-radius: 5px;
background: var(--darkgray);
}
.page-footer a {
display: flex;
margin-left: 4px;
}
<script src="https://unpkg.com/#lottiefiles/lottie-player#latest/dist/lottie-player.js"></script>
<header class="hiding-header">
<nav class="page-header">
<div class="trigger-menu-wrapper">
<button class="trigger-menu">
<svg width="12" height="12" viewBox="0 0 24 24">
<path d="M24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z" />
</svg>
<span>MENU</span>
</button>
</div>
<ul class="menu">
<li>
About
<ul class="sub-menu">
<li>
History
</li>
<li>
President
</li>
<li>
Team
</li>
<li>
Process
</li>
<li>
Clients
</li>
</ul>
</li>
</ul>
</nav>
</header>
<!-- <a href="" role="button" aria-label="Toggle menu" class="lottie-wrapper">
<lottie-player src="https://assets10.lottiefiles.com/datafiles/9gIwZ2uiiKglyb0/data.json" style="width: 60px; height: 60px;"></lottie-player>
</a> -->
<main class="page-main">
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/freedom.jpg);"></section>
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/travel.jpg);"></section>
<section style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/holidays.jpg);"></section>
</main>
<footer class="page-footer">
<!-- <span>made by </span>
<a href="https://georgemartsoukos.com/" target="_blank">
<img width="24" height="24" src="https://assets.codepen.io/162656/george-martsoukos-small-logo.svg" alt="George Martsoukos logo">
</a> -->
</footer>
Note: The biggest issues with approach is the scroll bars/width of the page can snap back and forth creating a jarring UI twitch. The second is that it clears the user scroll height, if they scroll to the bottom then open the menu and close it the scroll would jump back to the top. Since you hide the menu unless the user is at the top this doesn't matter in your case. The other issue is not noticeable in this case.

Make these slides loop?

I have been working on this for a little while, and I think I'm stuck at this point.
I made a div slider with arrows. Each div slide is set to a min-width of 80px.
The code works just fine, except, when I navigate either to the left or the right, it stops at the last item. I want this slider to loop (as in endlessly), instead of ending at the last item.
let buttonLeft = document.getElementById('slide_left')
let buttonRight = document.getElementById('slide_right')
buttonLeft.addEventListener('click', function() {
document.getElementById('slider').scrollLeft -= 90
})
buttonRight.addEventListener('click', function() {
document.getElementById('slider').scrollLeft += 90
})
body{
background-color: #555;
height: 100vh;
display: grid;
align-items: center;
justify-items: center;
font-family: 'Helvetica';
}
div#slide_wrapper{
width: 440px;
display: flex;
justify-content: space-between;
height: fit-content;
}
div#slider{
width: 350px;
display: flex;
height: fit-content;
flex-wrap: nowrap;
overflow: hidden;
}
div.thumbnail{
min-width: 80px;
min-height: 80px;
cursor: pointer;
display: grid;
place-items: center;
font-size: 30px;
}
div.thumbnail:not(:last-child){
margin-right: 10px;
}
div.thumbnail:nth-child(1){
background-color: darkturquoise;
}
div.thumbnail:nth-child(2){
background-color: goldenrod;
}
div.thumbnail:nth-child(3){
background-color: rebeccapurple;
}
div.thumbnail:nth-child(4){
background-color: powderblue;
}
div.thumbnail:nth-child(5){
background-color: firebrick;
}
div.thumbnail:nth-child(6){
background-color: sienna;
}
div.thumbnail:nth-child(7){
background-color: bisque;
}
div.thumbnail:nth-child(8){
background-color: navy;
}
div#slide_wrapper > button{
height: fit-content;
align-self: center;
font-size: 24px;
font-weight: 800;
border: none;
outline: none;
}
div#slide_wrapper > button:hover{
cursor: pointer;
background-color: dodgerblue;
color: #fff;
}
<div id="slide_wrapper">
<button id="slide_left" class="slide_arrow">❮</button>
<div id="slider">
<div class="thumbnail active">1</div>
<div class="thumbnail">2</div>
<div class="thumbnail">3</div>
<div class="thumbnail">4</div>
<div class="thumbnail">5</div>
<div class="thumbnail">6</div>
<div class="thumbnail">7</div>
<div class="thumbnail">8</div>
</div>
<button id="slide_right" class="slide_arrow">❯</button>
</div>
Nice slider! There are several ways to achieve what you want. In my opinion, the simplest solution would be to work with some if conditions and a counter variable. Once the number of thumbnails is reached, set the counter back to 0.
Update
Regarding to your comment. "How about doing the same for the left arrow.". In the if condition block: if (0 == slideCount) { i add these lines:
slideCount = thumbnail.length // set the slideCounter to maximum
document.getElementById('slider').scrollLeft = slideCount * 90 // calculate the scroll distance
slideCount--; // decrease slidecount
setActive(); // set the red border on the current slide
const buttonLeft = document.getElementById('slide_left')
const buttonRight = document.getElementById('slide_right')
const thumbnail = document.querySelectorAll(".thumbnail");
let slideCount = 0;
setActive();
buttonLeft.addEventListener('click', function() {
if (0 == slideCount) {
slideCount = thumbnail.length
document.getElementById('slider').scrollLeft = slideCount * 90
slideCount--;
setActive();
return;
}
slideCount--;
document.getElementById('slider').scrollLeft -= 90
setActive();
})
buttonRight.addEventListener('click', function() {
slideCount++;
if ( 8 == slideCount) {
document.getElementById('slider').scrollLeft = 0
slideCount = 0;
} else {
document.getElementById('slider').scrollLeft += 90
}
setActive();
})
function setActive() {
thumbnail.forEach(t => {
t.classList.remove("active");
})
thumbnail[slideCount].classList.add("active")
}
function count() {
console.log(slideCount);
}
body{
background-color: #555;
height: 100vh;
display: grid;
align-items: center;
justify-items: center;
font-family: 'Helvetica';
}
div#slide_wrapper{
width: 440px;
display: flex;
justify-content: space-between;
height: fit-content;
}
div#slider{
width: 350px;
display: flex;
height: fit-content;
flex-wrap: nowrap;
overflow: hidden;
}
div.thumbnail{
min-width: 80px;
min-height: 80px;
cursor: pointer;
display: grid;
place-items: center;
font-size: 30px;
}
div.thumbnail:not(:last-child){
margin-right: 10px;
}
div.thumbnail:nth-child(1){
background-color: darkturquoise;
}
div.thumbnail:nth-child(2){
background-color: goldenrod;
}
div.thumbnail:nth-child(3){
background-color: rebeccapurple;
}
div.thumbnail:nth-child(4){
background-color: powderblue;
}
div.thumbnail:nth-child(5){
background-color: firebrick;
}
div.thumbnail:nth-child(6){
background-color: sienna;
}
div.thumbnail:nth-child(7){
background-color: bisque;
}
div.thumbnail:nth-child(8){
background-color: navy;
}
div#slide_wrapper > button{
height: fit-content;
align-self: center;
font-size: 24px;
font-weight: 800;
border: none;
outline: none;
}
div#slide_wrapper > button:hover{
cursor: pointer;
background-color: dodgerblue;
color: #fff;
}
.active {
border: 2px solid red;
}
<div id="slide_wrapper">
<button id="slide_left" class="slide_arrow">❮</button>
<div id="slider">
<div class="thumbnail active">1</div>
<div class="thumbnail">2</div>
<div class="thumbnail">3</div>
<div class="thumbnail">4</div>
<div class="thumbnail">5</div>
<div class="thumbnail">6</div>
<div class="thumbnail">7</div>
<div class="thumbnail">8</div>
</div>
<button id="slide_right" class="slide_arrow">❯</button>
</div>
Just find the width of the slider and compare it with the scroll left like
let buttonLeft = document.getElementById('slide_left')
let buttonRight = document.getElementById('slide_right')
buttonLeft.addEventListener('click', function() {
document.getElementById('slider').scrollLeft -= 90
})
buttonRight.addEventListener('click', function() {
const elem = document.getElementById('slider');
const ElemWidth = elem.getClientRects()[0].width
if(document.getElementById('slider').scrollLeft > ElemWidth ){
document.getElementById('slider').scrollLeft = 0
}else{
document.getElementById('slider').scrollLeft += 90
}
console.log(ElemWidth, document.getElementById('slider').scrollLeft , document.getElementById('slider').scrollLeft > ElemWidth)
})

how to know child divs on container div center on scroll event right and left?

trying to do event 'scroll' and when in the callback function the record of position div only record of last position, i want to know if that div in the center target i want
const slide = document.querySelector(".slides")
slide.addEventListener('scroll', function(e) {
const slide2 = document.querySelector("#slide-2")
console.log(slide2.offsetLeft)
})
<div class="slider">
1
2
3
4
5
<div class="slides">
<div id="slide-1">1</div>
<div id="slide-2">2</div>
<div id="slide-3">3</div>
<div id="slide-4">4</div>
<div id="slide-5">5</div>
</div>
</div>
my goal here I want to know if a user on that div to Right and Left for my slider
so i can make active dots , i am trying to just use native javascript here :)
here is my Codepen example
https://codepen.io/lpllplp222/pen/BaRvwKm
I have used jQuery to achieve the same.
position() function from the jQuery provides the current position of an element from its top and left, I am using the left value to calculate the current active element and get its index, thereby providing an active class to the corresponding dot.
const slide = document.querySelector(".slides")
$('#slider-dots a').on('click', function(event) {
event.stopPropagation();
})
slide.addEventListener('scroll', function(e) {
var scrollLeft = $('#slides-wrapper').scrollLeft();
var currIndex = -1;
$('#slider-dots a').removeClass('active');
for(var i = 0; i<$('#slides-wrapper div').length; i++) {
if($($('#slides-wrapper div')[i]).position().left >= 0 && currIndex === -1) {
currIndex = i;
}
}
$($('#slider-dots a')[currIndex]).addClass('active');
})
* {
box-sizing: border-box;
}
.slider {
width: 400px;
text-align: center;
overflow: hidden;
}
.slides {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
/*
scroll-snap-points-x: repeat(300px);
scroll-snap-type: mandatory;
*/
}
.slides::-webkit-scrollbar {
width: 10px;
height: 10px;
}
#slider-dots a.active {
color: violet;
background-color: #000;
}
.slides::-webkit-scrollbar-thumb {
background: black;
border-radius: 10px;
}
.slides::-webkit-scrollbar-track {
background: transparent;
}
.slides > div {
scroll-snap-align: start;
flex-shrink: 0;
width: 300px;
height: 200px;
margin-right: 50px;
border-radius: 10px;
background: #eee;
transform-origin: center center;
transform: scale(1);
transition: transform 0.5s;
position: relative;
display: flex;
justify-content: center;
align-items: center;
font-size: 100px;
}
.slides > div:target {
/* transform: scale(0.8); */
}
.author-info {
background: rgba(0, 0, 0, 0.75);
color: white;
padding: 0.75rem;
text-align: center;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
margin: 0;
}
.author-info a {
color: white;
}
img {
object-fit: cover;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.slider > a {
display: inline-flex;
width: 1.5rem;
height: 1.5rem;
background: white;
text-decoration: none;
align-items: center;
justify-content: center;
border-radius: 50%;
margin: 0 0 0.5rem 0;
position: relative;
}
.slider > a:active {
top: 1px;
}
.slider > a:focus {
background: #000;
}
/* Don't need button navigation */
#supports (scroll-snap-type) {
.slider > a {
display: none;
}
}
html,
body {
height: 100%;
overflow: hidden;
}
body {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to bottom, #74abe2, #5563de);
font-family: "Ropa Sans", sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="slider" id='slider-dots'>
1
2
3
4
5
<div id='slides-wrapper' class="slides">
<div id="slide-1">
1
</div>
<div id="slide-2">
2
</div>
<div id="slide-3">
3
</div>
<div id="slide-4">
4
</div>
<div id="slide-5">
5
</div>
</div>
</div>

Vertical JavaScript Menu - Not All Subsections Displaying

Currently, I'm helping to build a website for a family member as part of a modeling club they have.
The website is at http://testindyamps.weebly.com/ .
It's a website on a host that utilizes various templates for themes (I haven't had much help so far from people on said site).
I'm not 100% sure if this ist he best place to post the question, but I thought I'd give it a shot.
The main issue is that it's utilizing a sidebar navigation where when you click the menus, it expands downward to show the subpages. In this case, not all the subpages are showing. (For example, clicking on "Articles" and then "Books" shows only the first few of a dozen or so pages.
I've tried editing the JS code itself, which so far has had no affect. I've tried editing some of the CSS, however, it doesn't seem to have an affect either.
If it helps, I can share osme of the CSS code or JS code for the site itself. Any help would be appreciated.
Thank you.
UPDATE: Added the code as requested.
Update 2: Added HTML: fixed to correct URL (using a test site instead of the "actual" site for the navigation).
jQuery(function($) {
// Mobile sidebars
$.fn.expandableSidebar = function(expandedClass) {
var $me = this;
$me.on('click', function() {
if(!$me.hasClass(expandedClass)) {
$me.addClass(expandedClass);
} else {
$me.removeClass(expandedClass);
}
});
}
// Interval loop
$.fn.intervalLoop = function(condition, action, duration, limit) {
var counter = 0;
var looper = setInterval(function(){
if (counter >= limit || $.fn.checkIfElementExists(condition)) {
clearInterval(looper);
} else {
action();
counter++;
}
}, duration);
}
// Check if element exists
$.fn.checkIfElementExists = function(selector) {
return $(selector).length;
}
// Check if desktop display
$.fn.isDesktop = function() {
return $(window).width() > 1024;
}
var briskController = {
init: function(opts) {
var base = this;
base._addClasses();
setTimeout(function(){
base._attachEvents();
}, 1000);
},
_addClasses: function() {
var base = this;
// Add fade in class to nav + logo + banner
$('body').addClass('fade-in');
// Keep subnav open if submenu item is active
$('.sidebar-nav .active').parents('.has-submenu').children('.dropdown').addClass('open');
// Add placeholder text to inputs
$('.wsite-form-sublabel').each(function(){
var sublabel = $(this).text();
$(this).prev('.wsite-form-input').attr('placeholder', sublabel);
});
},
_cloneLogin: function() {
var loginDetach = $('#member-login').clone(true);
$('.mobile-nav .wsite-menu-default > li:last-child').after(loginDetach);
},
_stickyNav: function() {
var sticky,
collapse,
uncollapse,
desktopsticky = $('body.nav-position-top.sticky-nav-on:not(.wsite-checkout-page):not(.wsite-native-mobile-editor), body.nav-position-top-right.sticky-nav-on:not(.wsite-checkout-page):not(.wsite-native-mobile-editor)').length,
mobilesticky = $('body.sticky-nav-on:not(.wsite-checkout-page):not(.wsite-native-mobile-editor)').length;
var stickyInit = function() {
if (!$.fn.isDesktop() || desktopsticky) {
// Add sticky desktop nav
sticky = new Waypoint.Sticky({
element: $('.header')[0]
});
}
if ($.fn.isDesktop() && desktopsticky) {
// Collapse header on scroll
collapse = new Waypoint({
element: $('body.nav-position-top.sticky-nav-on:not(.wsite-checkout-page):not(.wsite-native-mobile-editor)')[0],
handler: function(direction) {
$('body').addClass('collapse');
},
offset: -10
});
uncollapse = new Waypoint({
element: $('body.nav-position-top'),
handler: function(direction) {
$('body').removeClass('collapse');
},
offset: -5
});
}
}
stickyInit();
$(window).resize(function() {
if (sticky) { sticky.destroy() }
if (collapse) { collapse.destroy() }
if (uncollapse) { uncollapse.destroy() }
stickyInit();
});
},
_sidebarNav: function() {
// Fixed sidebar nav unless menu height exceeds viewport height
var sidebarCheck = function() {
if ($.fn.isDesktop() && $('body').hasClass('sticky-nav-on') && $('.header .container').height() + $('.header .contact').height() <= $(window).height() - 45) {
$('body.nav-position-sidebar .header').addClass('stuck');
}
else {
$('body.nav-position-sidebar .header').removeClass('stuck');
}
}
sidebarCheck();
$(window).resize(function() {
sidebarCheck();
});
},
_sidebarCart: function(){
$('#wsite-mini-cart').addClass('cart-init');
$('.wsite-nav-cart a').click(function() {
$('.cart-init').toggleClass('cart-visible');
});
$('.wrapper, .header').click(function() {
$('.cart-init').removeClass('cart-visible');
});
},
_attachEvents: function() {
var base = this;
// Hamburger nav toggle
$('.hamburger').on('click', function(e) {
e.preventDefault();
$('body').toggleClass('nav-open');
});
// Initialize sticky nav
base._stickyNav();
// Initialize sidebar nav
base._sidebarNav();
// Copy login
$.fn.intervalLoop('.mobile-nav #member-login', base._cloneLogin, 800, 5);
// Subnav toggle
$('li.has-submenu span.icon-caret, .dropdown-link').on('click', function() {
var $me = $(this);
if ($me.parent().hasClass('open')) {
$me.parent().removeClass('open');
$me.find('.open').removeClass('open');
}
else {
$('.open').removeClass('open');
$me.parents('.has-submenu').children('.dropdown').addClass('open');
}
setTimeout(function(){
base._sidebarNav();
}, 800);
});
// Sidebar Cart Link
$.fn.intervalLoop('.cart-init', base._sidebarCart, 1000, 5);
// Store category dropdown
$('.wsite-com-sidebar').expandableSidebar('sidebar-expanded');
// Search filters dropdown
$('#wsite-search-sidebar').expandableSidebar('sidebar-expanded');
// Init fancybox swipe on mobile
if ('ontouchstart' in window) {
$('body').on('click', 'a.w-fancybox', function() {
base._initSwipeGallery();
});
}
},
_initSwipeGallery: function() {
var base = this;
setTimeout(function(){
var touchGallery = document.getElementsByClassName('fancybox-wrap')[0];
var mc = new Hammer(touchGallery);
mc.on("panleft panright", function(ev) {
if (ev.type == "panleft") {
$("a.fancybox-next").trigger("click");
} else if (ev.type == "panright") {
$("a.fancybox-prev").trigger("click");
}
base._initSwipeGallery();
});
}, 500);
}
}
$(document).ready(function(){
briskController.init();
});
});
/* Header */
.header {
position: relative;
width: 100%;
color: #fill;
background: #bg;
border-bottom: 1px solid fade(#fill, 5);
box-sizing: border-box;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
z-index: 12;
.hamburger,
.contact,
.desktop-nav,
.sidebar-nav {
display: none;
}
}
body.nav-open {
overflow: hidden;
#media #tablet-up {
overflow: auto;
}
}
body:not(.nav-position-sidebar),
body.nav-position-top,
body.nav-position-top-right {
#media #tablet-up {
.header {
position: relative;
padding: 10px 40px;
border-bottom: none;
.transition(~'padding 280ms ease');
.container {
display: table;
overflow-y: hidden;
width: 100%;
height: 80px;
.transition(~'height 280ms ease');
}
.logo {
display: table-cell;
text-align: left;
vertical-align: middle;
max-height: 80px;
overflow: hidden;
a {
padding: 5px 20px 5px 0;
}
}
.desktop-nav {
display: table-cell;
}
.nav {
li {
display: inline-block;
}
a {
padding: 10px 20px;
}
}
.membership-cart {
display: table-cell;
width: 5%;
text-align: right;
white-space: nowrap;
span {
display: inline-block;
}
}
}
&.collapse {
.header {
padding: 5px 40px;
border-bottom: 1px solid fade(#fill, 5);
.container {
height: 40px;
}
}
}
&.full-width-nav-off .header .container {
max-width: 1200px;
margin: 0 auto;
padding: 0 40px;
box-sizing: border-box;
}
}
}
body.nav-position-top-right {
.desktop-nav {
text-align: right;
}
}
.stuck {
position: fixed !important;
top: 0;
}
body.nav-position-sidebar {
#media #tablet-up {
.header {
position: absolute;
top: 0;
left: 0;
width: 260px;
min-height: 100vh;
padding: 40px;
border-bottom: none;
display: flex;
flex-direction: row;
> .nav-wrap {
width: 100%;
min-height: calc(~'100vh - 80px');
display: flex;
flex-direction: column;
> .container {
flex: 1 0 auto;
}
}
.sidebar-nav {
display: block;
}
.nav {
li {
display: block;
}
a {
display: block;
padding: 10px 0;
}
}
.logo {
margin: 0 auto 30px;
}
.membership-cart > span {
display: block;
}
}
.contact {
display: block;
}
.wsite-phone {
display: block;
font-size: 15px;
color: fade(#fill, 40);
padding: 40px 0 0;
text-align: left;
&:before {
content: '';
display: block;
width: 60%;
padding-bottom: 40px;
border-top: 1px solid fade(#fill, 20);
}
}
.wrapper {
background: #bg;
padding-left: 260px;
box-sizing: border-box;
}
}
}
.logo {
* {
display: block;
}
a {
color: #primary;
&:hover {
opacity: 0.6;
background: transparent;
.transition(opacity 200ms ease);
}
}
#wsite-title {
font-family: #font1;
font-size: 30px;
font-weight: 500;
line-height: 1;
text-transform: uppercase;
letter-spacing: 0.08em;
}
img {
overflow: hidden;
max-width: 300px;
max-height: 70px;
}
.wsite-logo {
overflow: hidden;
max-width: 100%;
max-height: 70px;
}
}
/* Nav */
.nav {
vertical-align: middle;
a {
display: block;
color: #fill;
font-family: #font1;
font-size: 15px;
font-weight: 500;
line-height: 1;
letter-spacing: 0.05em;
text-transform: lowercase;
&:hover {
opacity: 0.6;
background: transparent;
.transition(opacity 200ms ease);
}
}
.active {
color: darken(#primary, 10%) !important;
}
#wsite-nav-cart-a {
padding-right: 0;
}
#wsite-nav-cart-num {
position: relative;
display: inline-block;
background: mix(#primary, #bg, 60%);
color: #fill;
min-width: 25px;
padding: 7px 2px;
text-align: center;
border-radius: 100%;
z-index: 2;
#media #tablet-up {
margin: 0 -6px;
}
}
}
.mobile-nav {
display: none;
}
/* Subnav */
#wsite-menus {
> .wsite-menu-wrap {
margin-top: 10px;
}
> .wsite-menu-wrap > .wsite-menu .wsite-menu {
margin: 0 -1px;
}
.wsite-menu {
position: relative;
background: #bg;
.box-shadow(inset 0px 0px 0px 1px fade(#fill, 3));
li a {
padding: 12px 20px;
background: transparent;
color: #fill;
font-family: #font1;
font-size: 14px;
font-weight: normal;
line-height: normal;
text-transform: lowercase;
letter-spacing: 0.05em;
border: none;
&:hover {
opacity: 0.6;
background: transparent;
.transition(opacity 200ms ease);
}
}
}
.wsite-menu-arrow {
display: none;
}
}
/* Sidebar and Mobile Subnav */
.sidebar-nav,
.mobile-nav {
li {
position: relative;
border-color: fade(#fill, 80);
}
.wsite-menu {
padding-left: 5px;
color: fade(#fill, 50);
border-color: fade(#fill, 50);
a {
color: fade(#fill, 50);
}
}
.wsite-menu-wrap {
display: block !important;
overflow: hidden;
max-height: 0;
.transition(all 600ms ease-in-out);
}
.wsite-menu-wrap li.wsite-nav-current > a.wsite-menu-subitem {
background: rgba(0, 0, 0, 0.95);
border: none;
}
.wsite-menu-wrap .wsite-menu-arrow {
display: none;
}
.dropdown {
display: table;
width: 100%;
&:hover {
.icon-caret {
opacity: 0.6;
background: transparent;
}
}
> .icon-caret,
> .dropdown-link {
display: table-cell !important;
vertical-align: top;
a {
display: inline-block !important;
}
}
.icon-caret {
width: 15px;
cursor: pointer;
.transition(all 200ms ease-in-out);
&:before {
content: '';
position: relative;
display: block;
width: 5px;
height: 5px;
border: solid transparent;
border-width: 0 1px 1px 0;
border-color: inherit;
.transform(~'rotate(45deg)');
}
}
&.open span.icon-caret:before {
top: 5px;
.transform(~'rotate(-135deg)');
}
&.open + .wsite-menu-wrap {
width: 100%;
max-height: 1000px;
}
}
}
.sidebar-nav {
.has-submenu > .dropdown span.icon-caret {
padding: 12px 0 8px 10px;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="no-header-page">
<div class="header">
<div class="nav-wrap">
<div class="container">
<a class="hamburger" aria-label="Menu" href="#"><span></span></a>
<div class="logo">{logo}</div>
<div class="nav desktop-nav">{menu}</div>
<div class="nav sidebar-nav"><div class="nav-wrap">{menu}</div></div>
<div class="nav membership-cart">{membership}{minicart}</div>
</div>
<div class="nav contact">{phone:text}</div>
</div>
</div>
<div class="wrapper">
<div class="main-wrap">
{{#sections}}
<div class="container">{content}</div>
{{/sections}}
</div>
<div class="footer-wrap">
<div class="footer">{footer}</div>
</div>
</div>
<div class="nav mobile-nav">
<a class="hamburger" aria-label="Menu" href="#"><span></span></a>
{menu}
</div>
<script type="text/javascript" src="/files/theme/plugins.js"></script>
<script type="text/javascript" src="/files/theme/custom.js"></script>
</body>
</html>
The reason not all elements are showing is because of this:
.sidebar-nav .dropdown.open + .wsite-menu-wrap, .mobile-nav .dropdown.open + .wsite-menu-wrap {
width: 100%;
max-height: 1000px;
}
The element is expanded by changing the max-height from 0px to 1000px. The elements in your menu exceed 1000px and they get cut off.
This is actually a pretty comman problem when using CSS transitions to expand elements. CSS transitions only work on height if height is set to an exact value. You can read more about it here: https://css-tricks.com/using-css-transitions-auto-dimensions/
The max-height trick offers a work-around. But it has its draw-back - if the elements expands beyond the value of max-height it gets cut off.
The simplest solution is to simply increase the value of max-height, until all your elements show. This will work, but it' s not ideal if in the future the element expands even more.
More sophisticated (and arguably better) solutions can be found in the css-tricks web page above.
Still, are you sure it's a good idea do display such an enormous amount of of links in an accordion menu? Wouldn't it be better to rethink the navigation, perhaps have a separate page with all the books?

Categories

Resources