当用户手动滚动时,顶部菜单中的活动指示保持同步



我有一个HTML页面,其中有几个部分,用户可以通过单击顶部的相应链接导航到这些部分。当用户通过该链接导航时,该链接将被突出显示。

现在,我希望当用户手动向上或向下滚动,并且看到不同的部分时,顶部菜单中的相应链接将得到突出显示。

我的尝试是在scroll事件处理程序中进行的,但在查找与当前滚动位置相对应的HTML节锚id时遇到了问题。

请注意,我需要动态生成li,这一点不能改变:根据我的项目要求,ul必须为空,因此所有列表项和超链接都必须用JavaScript生成(就像目前所做的那样(。

这是我的页面。我的问题出现在scroll事件处理程序中:

/**
* 
* Manipulating the DOM exercise.
* Exercise programmatically builds navigation,
* scrolls to anchors from navigation,
* and highlights section in viewport upon scrolling.
* 
* Dependencies: None
* 
* JS Version: ES2015/ES6
* 
* JS Standard: ESlint
* 
*/
/**
* Define Global Variables
* 
*/

//  All sections - Navigation Bar - Fragment & AllLinks
const navigationBar = document.getElementById('navbar__list');
var navigationBarContainer = document.getElementById("landing__container");
const fragment = document.createDocumentFragment();
const pagesections = document.querySelectorAll('section');
const navigationBarLi = document.querySelectorAll('nav .landing__container ul li');
// adding new classes to sections to match with anchor id for scroll purposes
//document.getElementById('li').className('section')

// for each single section
/*function retrieveElementsById(ids) {
var listIds = ids.split(" ");
var arrayresults = [], item;
for (var i = 0; i < listIds.length; i++){
item = document.getElementById(idList[i]);
if (item) {
results.push(item);
} 
}
return(results);
}


allSectionsinSingFnct(querySelectorAll("section1 section2 section3 section4 section5 section6 section7"))
*/
//
function sectionsidattr() {
var sec1 = document.getElementById("section1");
var sec2 = document.getElementById("section2");
var sec3 = document.getElementById("section3");
var sec4 = document.getElementById("section4");
var sec5 = document.getElementById("section5");
var sec6 = document.getElementById("section6");
var sec7 = document.getElementById("section7");
}
const sectionsids = [
'section1', '#section1',
'section2', '#section2',
'section3', '#section3',
'section4', '#section4',
'section5', '#section5',
'section6', '#section6',
'section7', '#section7'
]
//const elements = document.querySelectorAll(ids.map(id => `#${id}`).join(', '));
//const sectionidelements = document.querySelectorAll(sectionids.map(id => `#${id}`).join(', '))

/**
* End Global Variables
* Start Helper Functions
*
*/
/**
* End Helper Functions
* Begin Main Functions
*
*/

/* Reference
https://stackoverflow.com/questions/65407419/how-to-add-active-class-to-the-list-href-that-equal-showed-in-the-viewport-sec
*/
// Building the Navigation Bar
for (let pagesection of pagesections) {
const pagelist = document.createElement('li');
const pagelinks = document.createElement('a');
const pagesectionId = pagesection.getAttribute('id');
const pagesectionTitle = pagesection.getAttribute('data-nav');
pagelinks.classList = 'menu__link';
pagelinks.setAttribute('href', `#${pagesectionId}`);
pagelinks.innerText = pagesectionTitle;
fragment.appendChild(pagelist);
pagelist.appendChild(pagelinks);

// Smooth Scroll   
pagelinks.addEventListener('click', function (event) {
event.preventDefault();
window.scrollTo({
top: pagesection.offsetTop - 50,
behavior: 'smooth'
});
});
}
navigationBar.appendChild(fragment);

const allpageLinks = navigationBar.querySelectorAll('a');
//Scroll to using anchor ID
var scrollanchorid = document.querySelectorAll("section");
function scrollToAnchor() {
scrollToAnchor.scrollIntoView(true);
}
// Set the section as active when it is in the scope of the screen.
// vars at top pagesections & navigationBarLi

// when scrolling run the following function
window.addEventListener('scroll', () => {
let current = ''; // no current section at the beginning
//looping through all the sections
pagesections.forEach(section => {
const topSction = section.offsetTop;
//added for future reference that the logic loops through the values of the page 
//console.log(topSction);
//retrieve sectionHeight
console.log(pageYOffset);
const sctionHeight = section.clientHeight;
if (pageYOffset >= (topSction - sctionHeight / 1)) {
current = section.getAttribute('id');
}
//Page Y Offset means how much we are scrolled here

})

//End of for each loop 
// added for future refernce  
//console.log(current);
navigationBarLi.forEach(li => {
li.classList.remove('active');
if (li.classList.contains('current')) {
li.classList.add('active')
}
})
// Add class 'active' to section when near top of viewport 
// Another for each loop for active classes
const links = document.querySelectorAll('li');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener("click", function () {
var currenstate = document.getElementsByClassName("active");
currenstate[0].className = currenstate[0].className.replace("active", "");
this.className += "active";
});
};
});






//Set the menu item as active when the corresponding section is active.
/*Reference
https://www.w3schools.com/howto/howto_js_active_element.asp
*/



// Return Top Button 
function scrollTopfunction() {
if (document.body.scrollTop > 25 ||
document.documentElement.scrollTop > 25) {
TopButton.style.display = "block";
} else {
TopButton.style.display = "none";
}
}

// At user click return to top of page for chrome , safari , firefox & most modern browsers
function pushtopfunction() {
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
}

/* User time out function when idle at 500 seconds */
setTimeout(function () { alert("User Time Out Message : 5 minutes"); }, 500000);
/*
*
* CSS written based on SMACSS architecture.
* To learn more, visit: http://smacss.com/
* 
* For simplicity, no reset or normalize is added. 
* To learn more, visit: https://css-tricks.com/reboot-resets-reasoning/
*
*/

@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap');
/* ---- Base Rules ---- */

*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
html{
font-family: 'Roboto', sans-serif;
scroll-behavior: smooth;
}

body {
margin: 0;
background: linear-gradient(to bottom, #c31432, #240b36);
color: #fff;
}
ul {
margin: 0;
padding: 0;
overflow: hidden;
background: linear-gradient(to right top, #abbaab, #a34444);
}

section {
position: relative;
min-height: 100vh;
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
}
img {
max-width: 100%;
}
button {
cursor: pointer;
border-radius: 12.5px;
}
/* Typeography General*/
header {
position: absolute;
top: 5px;
bottom: 5px;
align-items: center;
text-align: center;
background: transparent;
text-decoration: blanchedalmond;
text-shadow: black;
}
h1 {
font-family: 'Fira Sans', sans-serif;
border-radius: 50%;
border-color: black;
text-align: center;
align-items: center;
color: goldenrod;
background: linear-gradient(to right top, #8e0e00, #1f1c18);
}
.main__hero {
font-family: 'Fira Sans', sans-serif;
font-size: large;
top: 85px;
float: middle;
align-items: center;
border-color: black;
text-align: center;
color: black;
position: sticky;
}
@media only screen and (min-width: 35em) {
h1 {
font-size: large;
text-align: center;
align-items: center;
color: goldenrod;
}
}
h2 {
border-bottom: 1px solid #cc1;
font-family: 'Oxygen', Sans-Serif;
font-size: 1.2em;
color: azure;
}
p {
line-height: 1.5em;
font-size: 0.9em;
color: #eee;
word-spacing: 0.1em;
}
/* ---- Layout Rules ---- */
main {
margin: 10vh 1em 10vh;
}
.main-hero {
min-height: 40vh;
padding-top: 3em;
}

/* Some sections features*/
section h2 {
display: sticky;
font-size: 7.7vh;
top: 10px;
bottom: 7px;
text-shadow: black;
color: cornsilk;
}
section h3 {
display: flex;
font-size: 2.0rem;
top: 10px;
bottom: 7px;
text-shadow: black;
color: burlywood;
}
/* ---- Module Rules ---- */

/* Return Top Button*/
.topbtnclass {
padding: 15px;
bottom: 20px;
right: 10px;
position: fixed;
text-align: center;
border-radius: 15px;
outline: none;
color: white;
z-index: 99;
font-size: 12.5px;
cursor: pointer;
align-items: center;
background: linear-gradient(to right bottom, #304352, #d7d2cc);
font-family: 'Oxygen', Sans-Serif;
}
/* Return Top Button Hover */
.topbtnclass:hover {
color: goldenrod;
display: flex;
background: linear-gradient(to right bottom, #870000, #190a05);
}
/* Adding an active class */
.active, .a:hover {
background: linear-gradient(to right bottom, #852121, #190a05);
color: goldenrod;
border-radius: 50%;
}
/* Navigation Styles*/
.navbar__menu {
position: fixed;
top: 0px;
float: middle;
background: linear-gradient(to right bottom, #304352, #d7d2cc);
width: 100%;
max-width: 2150px;
margin:0 auto;
text-align: center;
padding: 10px;
}
.navbar__menu ul {
position: relative;
padding-left: 1px;
padding-right: 1px;
text-align: center;
display: inline-block;
background: linear-gradient(to right bottom, #304352, #d7d2cc);
border-radius: 90%;
}
.navbar__menu li {
display: inline-block;
position: relative;

}

.navbar__menu li ul a {
display: inline-block;
padding: 7px 14px;
text-decoration: none;
}
.navbar__menu li ul.active{
background: linear-gradient(to right bottom, #c31432, #240b36);
color: goldenrod;
}

.navbar__menu .menu__link {
display: flex;
padding: 1em;
font-weight: bold;
text-decoration: none;
text-shadow: black;
color: goldenrod;
border-radius: 50%;
}
.navbar__menu .menu__link:hover {
color: wheat;
transition: ease 0.3s all;
}


/* Header Styles */
.page__header {
background: linear-gradient(to right bottom, #c31432, #240b36);
position: sticky;
font-size: 2vw;
width: 100%;
z-index: 50;
border-color: black;
}
/* Footer Styles */
.page__footer {
background: #000;
padding: 3em;
color: #fff;
}
.page__footer p {
color: #fff;
}
/* ---- Theme Rules ---- */
/* Landing Container Styles */
.landing__container {
padding: 1em;
text-align: center;
align-items: center;
}
@media only screen and (min-width: 35em) {
.landing__container {
max-width: 50em;
padding: 4em;
}
}
section:nth-of-type(even) .landing__container {
text-align: center;
align-items: center;
}
/* Background Circles */
/* Note that background circles are created with psuedo elements before and after */
/* Circles appear to be random do to use of :nth-of-type psuedo class */
section:nth-of-type(odd) .landing__container::before {
content: '';
background: rgba(255, 255, 255, 0.187);
position: fixed;
z-index: -5;
width: 50vh;
height: 50vh;
border-radius: 50%;
opacity: 0;
transition: ease 0.5s all;
}
section:nth-of-type(even) .landing__container::before {
content: '';
background: rgb(255, 255, 255);
background: linear-gradient(0deg, rgba(255, 255, 255, .1) 0%, rgba(255, 255, 255, .2) 100%);
position: fixed;
top: 3em;
right: 3em;
z-index: -5;
width: 40vh;
height: 40vh;
border-radius: 50%;
opacity: 0;
transition: ease 0.5s all;
}
section:nth-of-type(3n) .landing__container::after {
content: '';
background: rgb(255, 255, 255);
position: absolute;
right: 0;
bottom: 0;
z-index: -5;
width: 10vh;
height: 10vh;
border-radius: 50%;
opacity: 0;
transition: ease 0.5s all;
}
section:nth-of-type(3n + 1) .landing__container::after {
content: '';
background: rgb(255, 255, 255);
position: absolute;
right: 20vw;
bottom: -5em;
z-index: -5;
width: 15vh;
height: 15vh;
border-radius: 50%;
opacity: 0;
transition: ease 0.5s all;
}
/* ---- Theme State Rules ---- */
/* Section Active Styles */
/* Note: your-active-class class is applied through javascript. You should update the class here and in the index.html to what you set in your javascript file.  */
section.active {
color: goldenrod;
background: linear-gradient(to right bottom, #870000, #190a05);
}
section.active .landing__container::before {
opacity: 1;
animation: rotate 4s linear 0s infinite forwards;
}
section.active .landing__container::after {
opacity: 1;
animation: rotate 5s linear 0s infinite forwards reverse;
}
/* Section Active Styles Keyframe Animations */
@keyframes rotate {
from {
transform: rotate(0deg) translate(-1em) rotate(0deg);
}
to {
transform: rotate(360deg) translate(-1em) rotate(-360deg);
}
}
/* Active Class */
li a.active {
color: goldenrod;
}
.active {
color: goldenrod;
background: linear-gradient(to right bottom, #870000, #190a05);
cursor: pointer;
position: relative;
}
.active:hover {
color: goldenrod;
background: linear-gradient(to right bottom, #870000, #190a05);
position: relative;
}
/*Page sections Gradients by id*/
#section1 {
/*border-radiuse at 25% to makesections more rounded*/
border-radius: 25%;
background-color: #fdb813;
background-image: linear-gradient(315deg, #fdb813 0%, #788cb6 74%);
}

#section2 {
border-radius: 25%;
background-color: #edd812;
background-image: linear-gradient(315deg, #edd812 0%, #766a65 74%);

}

#section3 {
border-radius: 25%;
background-color: #edd812;
background-image: linear-gradient(315deg, #edd812 0%, #766a65 74%);
}

#section4 {
border-radius: 25%;
background-color: #fdb813;
background-image: linear-gradient(315deg, #fdb813 0%, #788cb6 74%);
}
#section5 {
border-radius: 25%;
background-color: #edd812;
background-image: linear-gradient(315deg, #edd812 0%, #766a65 74%);
}

#section6 {
border-radius: 25%;
background-color: #b3cdd1;
background-image: linear-gradient(315deg, #b3cdd1 0%, #9fa4c4 74%);
}

#section7 {
border-radius: 25%;
background-color: #eaf818;
background-image: linear-gradient(315deg, #eaf818 0%, #f6fc9c 74%);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Manipulating the DOM</title>
<!-- Load Google Fonts -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans:900|Merriweather&display=swap" rel="stylesheet">
<!-- Load Styles -->
<link href="css/styles.css" rel="stylesheet">
</head>
<!-- HTML Follows BEM naming conventions 
IDs are only used for sections to connect menu achors to sections -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans:900|Merriweather&display=swap" rel="stylesheet">
<!-- HTML Follows BEM naming conventions 
IDs are only used for sections to connect menu achors to sections -->
<header class="page__header">
<nav class="navbar__menu">
<!-- Navigation starts as empty UL that will be populated with JS -->
<div class="menucontainer">
<ul id="navbar__list"></ul>
</div>

</nav>
</header>
<main>
<header class="main__hero">
<h1>Landing Page </h1>
</header>
<!-- Each Section has an ID (used for the anchor) and 
a data attribute that will populate the li node.
Adding more sections will automatically populate nav.
The first section is set to active class by default -->
<section id="section1" data-nav="Section 1">
<div class="landing__container">
<h2>Section 1</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam fugit nobis eaque sunt aperiam molestiae cum in sapiente placeat iusto debitis expedita alias non,
vitae velit cumque exercitationem, minima consectetur.
</p>
</div>
</section>
<section id="section2" data-nav="Section 2">
<div class="landing__container">
<h2>Section 2</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam fugit nobis eaque sunt aperiam molestiae cum in sapiente placeat iusto debitis expedita alias non,
vitae velit cumque exercitationem, minima consectetur.
</p>
</div>
</section>
<section id="section3" data-nav="Section 3">
<div class="landing__container">
<h2>Section 3</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam fugit nobis eaque sunt aperiam molestiae cum in sapiente placeat iusto debitis expedita alias non,
vitae velit cumque exercitationem, minima consectetur.
</p>
</div>
</section>
<section id="section4" data-nav="Section 4">
<div class="landing__container">
<h2>Section 4</h2>
<img src="https://cdn.pixabay.com/photo/2015/01/15/16/16/staircase-600468_1280.jpg" alt=:"staircase-600468_1280"
width="150" height="150">
<img src="https://cdn.pixabay.com/photo/2015/12/08/00/39/steps-1081909_1280.jpg" alt=:"steps-1081909_1280"
width="150" height="150">
<img src="https://cdn.pixabay.com/photo/2014/03/08/22/32/escalator-283448_1280.jpg"
alt=:"escalator-5899073_1280" width="150" height="150">
</div>
</section>
<section id="section5" data-nav="Section 5">
<div class="landing__container">
<h2>Section 5</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam fugit nobis eaque sunt aperiam molestiae cum in sapiente placeat iusto debitis expedita alias non,
vitae velit cumque exercitationem, minima consectetur.
</p>
</div>
</section>
<section id="section6" data-nav="Section 6">
<div class="landing__container">
<h2>Section 6</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Quisquam fugit nobis eaque sunt aperiam molestiae cum in sapiente placeat iusto debitis expedita alias
non,
vitae velit cumque exercitationem, minima consectetur.
</p>
</div>
</section>
<section id="section7" data-nav="Section 7" class="active">
<div class="landing__container">
<h2>Section 7</h2>
<label for"email">Email:</label>
</br>
<input type="text" name="email">
</br>
<label for"Last_name.">Last Name:</label>
</br>
<input type="text" name="Last_name">
</br>
<label for"First_name.">First Name:</label>
</br>
<input type="text" name="First_name."></br>
</form>
</br>
</br>
<textarea class="text_area_class" rows="25" cols=50>
Enterinquiry here.
</textarea>
</br>
<input type="submit">
<br>
</div>
</section>
<button class="topbtnclass" onclick="pushtopfunction()">Top</button>
<script src="js/app.js">
</script>
</main>
<footer class="page__footer">
<p>&copy Udacity</p>
</footer>

存在一些问题:

  • 表达式pageYOffset >= (topSction - sctionHeight / 1)不正确。从截面的顶部偏移中减去截面的高度是没有意义的。相反,使循环可中断(使用for而不是forEach(,并检查是否为pageYOffset + top < topSction + sctionHeight。如果是这样的话,就不再需要进一步搜索了:我们找到了部分。

  • 考虑到顶部的菜单栏会使部分区域不可见,因此请确保上面这一点的逻辑将此偏移量考虑在内。将top定义为menu.offsetTop + menu.clientHeight,其中menu.navbar__menu元素。

  • li.classList.contains('current')是一个永远不成立的条件,因为您没有currentCSS类。您希望在此处引用变量current,但请改用文字字符串。此外,current的值(例如"section3"(将不会在li元素的类列表中找到。您可以在子a元素的href属性中查找它:

    li.children[0].href.endsWith(current)
    
  • navigationBarLi集合为空,因为您在尚未构建菜单的时刻初始化了此变量。相反,您可以使用navigationBar.children,它将动态查找li元素。

综合来看,在滚动事件处理程序的第一部分中使用以下代码:

let current = '';
const menu = document.querySelector('.navbar__menu');
const top = menu.offsetTop + menu.clientHeight; // Add this offset

for (let section of pagesections) { // make a for-loop, so you can break out
const topSction = section.offsetTop;
const sctionHeight = section.clientHeight;
if (pageYOffset + top < topSction + sctionHeight) { // compare with end of section
current = section.getAttribute('id');
break; // <-- add break
}
}
for (let li of navigationBar.children) { // the collection you had was empty
li.classList.remove('active');
if (li.children[0].href.endsWith(current)) { // This identifies the section's li
li.classList.add('active');
}
}

最新更新