Vanilla JS在滚动重构时更改链接的活动状态 [英] Vanilla JS change active state of links when scrolling refactoring
问题描述
我正在尝试从即将进行的项目中放弃jQuery.我找到了一种使用纯Vanilla JS在滚动上创建粘性导航的方法,并希望导航中的链接在到达相应部分时更改其活动状态.
下面是我想出的可行解决方案,但是我确信可以改进代码以编程方式工作,而不必为每个元素选择和创建条件.
我也使用了许多不同的选择器,我很确定必须有一种方法可以改善它,例如使用querySelectorAll
并在forEach
循环中添加eventListener
,但是我可以也不能使它正常工作.感谢您的帮助!
// select links
const allLinks = document.querySelectorAll('header nav ul li');
const linkTop = document.querySelector('#linkTop');
const linkAbout = document.querySelector('#linkAbout');
const linkServices = document.querySelector('#linkServices');
const linkClients = document.querySelector('#linkClients');
const linkContact = document.querySelector('#linkContact');
// select sections
const sectionTop = document.querySelector('#top');
const sectionAbout = document.querySelector('#about');
const sectionServices = document.querySelector('#services');
const sectionClients = document.querySelector('#clients');
const sectionContact = document.querySelector('#contact');
function changeLinkState() {
// home
if (window.scrollY >= sectionTop.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkTop.classList.add('active');
}
// about
if (window.scrollY >= sectionAbout.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkAbout.classList.add('active');
}
// services
if (window.scrollY >= sectionServices.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkServices.classList.add('active');
}
clients
if (window.scrollY >= sectionClients.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkClients.classList.add('active');
}
contact
if (window.scrollY >= sectionContact.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkContact.classList.add('active');
}
}
window.addEventListener('scroll', changeLinkState);
<nav>
<ul>
<li id="linkTop">
<a href="#top">Home</a>
</li>
<li id="linkAbout">
<a href="#about">About Us</a>
</li>
<li id="linkServices">
<a href="#services">Services</a>
</li>
<li id="linkClients">
<a href="#clients">Clients</a>
</li>
<li id="linkContact">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
非常感谢!
您可以使用document.querySelectorAll()
获取所有部分和链接.滚动浏览时,从最后到第一部分遍历该部分的列表,直到找到匹配的部分.然后从所有链接中删除.active
类,并将其添加到活动index
处的链接中.
注意:您应使用限制以防止调用一秒钟内多次changeLinkState
.另一种选择是使用交集观察者API .>
const links = document.querySelectorAll('.links');
const sections = document.querySelectorAll('section');
function changeLinkState() {
let index = sections.length;
while(--index && window.scrollY + 50 < sections[index].offsetTop) {}
links.forEach((link) => link.classList.remove('active'));
links[index].classList.add('active');
}
changeLinkState();
window.addEventListener('scroll', changeLinkState);
nav {
position: fixed;
top: 0;
right: 0;
width: 10em;
}
section {
height: 100vh;
margin: 1em 0;
background: gold;
}
.active {
background: silver;
}
<nav>
<ul>
<li id="linkTop" class="links">
<a href="#top">Home</a>
</li>
<li id="linkAbout" class="links">
<a href="#about">About Us</a>
</li>
<li id="linkServices" class="links">
<a href="#services">Services</a>
</li>
<li id="linkClients" class="links">
<a href="#clients">Clients</a>
</li>
<li id="linkContact" class="links">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
<section>
Home
</section>
<section>
About Us
</section>
<section>
Services
</section>
<section>
Clients
</section>
<section>
Contact
</section>
I'm trying to ditch jQuery from my upcoming projects. I found a way to create a sticky nav on scroll with pure Vanilla JS, and wanted the links on my navigation to change their active state as they reach the corresponding sections.
Below is the working solution I came up with, but I'm sure that the code can be improved to work programmatically without having to select and create conditions for each element.
I'm also using a lot of different selectors, and I'm pretty sure there must be a way to improve that, maybe using querySelectorAll
and adding an eventListener
in a forEach
loop, but I can't get that to work either. Thanks for your help!
// select links
const allLinks = document.querySelectorAll('header nav ul li');
const linkTop = document.querySelector('#linkTop');
const linkAbout = document.querySelector('#linkAbout');
const linkServices = document.querySelector('#linkServices');
const linkClients = document.querySelector('#linkClients');
const linkContact = document.querySelector('#linkContact');
// select sections
const sectionTop = document.querySelector('#top');
const sectionAbout = document.querySelector('#about');
const sectionServices = document.querySelector('#services');
const sectionClients = document.querySelector('#clients');
const sectionContact = document.querySelector('#contact');
function changeLinkState() {
// home
if (window.scrollY >= sectionTop.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkTop.classList.add('active');
}
// about
if (window.scrollY >= sectionAbout.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkAbout.classList.add('active');
}
// services
if (window.scrollY >= sectionServices.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkServices.classList.add('active');
}
clients
if (window.scrollY >= sectionClients.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkClients.classList.add('active');
}
contact
if (window.scrollY >= sectionContact.offsetTop) {
allLinks.forEach(link => {
link.classList.remove('active');
});
linkContact.classList.add('active');
}
}
window.addEventListener('scroll', changeLinkState);
<nav>
<ul>
<li id="linkTop">
<a href="#top">Home</a>
</li>
<li id="linkAbout">
<a href="#about">About Us</a>
</li>
<li id="linkServices">
<a href="#services">Services</a>
</li>
<li id="linkClients">
<a href="#clients">Clients</a>
</li>
<li id="linkContact">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
Thanks a lot!
You can get all sections and links using document.querySelectorAll()
. On scroll iterate the list of section from last to first, until you find one that matches. Then remove the .active
class from all links, and add it to the link at the active index
.
Note: You should use throttling to prevent the calling of changeLinkState
multiple times in a second. Another option is to use the Intersection Observer API.
const links = document.querySelectorAll('.links');
const sections = document.querySelectorAll('section');
function changeLinkState() {
let index = sections.length;
while(--index && window.scrollY + 50 < sections[index].offsetTop) {}
links.forEach((link) => link.classList.remove('active'));
links[index].classList.add('active');
}
changeLinkState();
window.addEventListener('scroll', changeLinkState);
nav {
position: fixed;
top: 0;
right: 0;
width: 10em;
}
section {
height: 100vh;
margin: 1em 0;
background: gold;
}
.active {
background: silver;
}
<nav>
<ul>
<li id="linkTop" class="links">
<a href="#top">Home</a>
</li>
<li id="linkAbout" class="links">
<a href="#about">About Us</a>
</li>
<li id="linkServices" class="links">
<a href="#services">Services</a>
</li>
<li id="linkClients" class="links">
<a href="#clients">Clients</a>
</li>
<li id="linkContact" class="links">
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
<section>
Home
</section>
<section>
About Us
</section>
<section>
Services
</section>
<section>
Clients
</section>
<section>
Contact
</section>
这篇关于Vanilla JS在滚动重构时更改链接的活动状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!