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><a href="#intro"><button>Introduction</button></a></li>
<li><a href="#first"><button>1. User research</button></a></li>
<li><a href="#second"><button>2. Ideation</button></a></li>
<li><a href="#third"><button>3. Design phase</button></a></li>
<li><a href="#fourth"><button>4. Testing</button></a></li>
<li><a href="#fifth"><button>5. Results</button></a></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>