I have a nav which contains letters and sections which are associated to letters in the nav.
When a user scrolls to a section, I want to addClass active to that letter. For example:
section with the id of a, the anchor with the data-letter with a will be active.Currently, on scroll, all my letters in the nav become active and this is because it's always thinking it's on section A.
Demo:
$(function() {
$(window).scroll(function() {
// step 1: get id of section
var visible_section = $('section:visible'), id = visible_section.attr('id');
console.log(id);
// step 2: add class where id and data-letter match
$("nav a").removeClass("active");
$("nav a[data-letter='"+id+"']").addClass("active");
});
});
.nav {
background: grey;
padding: 30px 15px;
position: fixed;
top: 0;
width: 100%;
}
.nav a {
padding: 0 15px;
text-decoration: none;
}
.nav a:hover {
background: black;
color: white;
}
.nav a.active {
background: black;
color: white;
}
.sections {
margin-top: 100px;
}
section {
padding: 200px 0;
color: red;
display: flex;
align-items: center;
justify-content: center;
font-size: 50px;
}
section:nth-child(odd) {
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<main>
<nav class="nav">
<a href="#a" data-letter="a">A</a>
<a href="#b" data-letter="b">B</a>
<a href="#c" data-letter="c">C</a>
<a href="#d" data-letter="d">D</a>
</nav>
<div class="sections">
<section id="a">A</section>
<section id="b">B</section>
<section id="c">C</section>
<section id="d">D</section>
</div>
</main>
FYI: https://stackoverflow.com/a/5354536/4571790
function isVisible(elm) {
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
}
$(function() {
$(window).scroll(function() {
// step 1: get id of section
var visible_section = $('section:visible'), id="";
// this code will find which section is the first visible
$(".sections").find("section").each((i,a)=>id==""?(isVisible(a)?id=$(a).attr("id"):id):id);
$("#result").html(id +" is visible now");
//console.log(id);
// step 2: add class where id and data-letter match
$("nav a").removeClass("active");
$("nav a[data-letter='"+id+"']").addClass("active");
});
});
.nav {
background: grey;
padding: 30px 15px;
position: fixed;
top: 0;
width: 100%;
}
.nav a {
padding: 0 15px;
text-decoration: none;
}
.nav a:hover {
background: black;
color: white;
}
.nav a.active {
background: black;
color: white;
}
.sections {
margin-top: 100px;
}
section {
padding: 200px 0;
color: red;
display: flex;
align-items: center;
justify-content: center;
font-size: 50px;
}
section:nth-child(odd) {
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<main>
<nav class="nav">
<a href="#a" data-letter="a">A</a>
<a href="#b" data-letter="b">B</a>
<a href="#c" data-letter="c">C</a>
<a href="#d" data-letter="d">D</a>
<span id="result"></span>
</nav>
<div class="sections">
<section id="a">A</section>
<section id="b">B</section>
<section id="c">C</section>
<section id="d">D</section>
</div>
</main>
You should differentiate the nav tabs by setting their data-letter as you described to a, b, c and d:
<nav class="nav">
<a href="#a" data-letter="a">A</a>
<a href="#b" data-letter="b">B</a>
<a href="#c" data-letter="c">C</a>
<a href="#d" data-letter="d">D</a>
</nav>
All your nav become active not because it always thinks it's on a, but because all data-letter are set to a.
You can change ratio from 0.1 to 1:
$(function () {
let ratio = 0.6; // From 0.1 to 1
$(window).scroll(function () {
let sections = $('.sections section');
let scrollTop = $(window).scrollTop();
let screen_height = $(window).height();
$.each(sections, function () {
let top = $(this).offset().top;
let calc = (top - scrollTop) / screen_height;
if (calc >= (ratio * -1) && calc <= ratio) {
let id = $(this).attr('id');
$("nav a").removeClass("active");
$("nav a[data-letter='" + id + "']").addClass("active");
//console.log(`${id} is Active`);
}
});
});
});
.nav {
background: grey;
padding: 30px 15px;
position: fixed;
top: 0;
width: 100%;
}
.nav a {
padding: 0 15px;
text-decoration: none;
}
.nav a:hover {
background: black;
color: white;
}
.nav a.active {
background: black;
color: white;
}
.sections {
margin-top: 100px;
}
section {
padding: 200px 0;
color: red;
display: flex;
align-items: center;
justify-content: center;
font-size: 50px;
}
section:nth-child(odd) {
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main>
<nav class="nav">
<a href="#a" data-letter="a">A</a>
<a href="#b" data-letter="b">B</a>
<a href="#c" data-letter="c">C</a>
<a href="#d" data-letter="d">D</a>
</nav>
<div class="sections">
<section id="a">A</section>
<section id="b">B</section>
<section id="c">C</section>
<section id="d">D</section>
</div>
</main>