Fix my querySelector to add a specific class to it in javaScript

Viewed 165

I have built the navigation bar in my web page dynamically as shown below

NavBuilder = () => {
//to loop from section 1 to 4 and add sections to navigation 
for (s = 0; s < sections.length; s++) {
    const liElem = document.createElement('li');
    //const divElem = document.createElement("div");
    const Id = sections[s].id;
    const DataNav = sections[s].dataset.nav;
    liElem.innerHTML = `<a class="menu__link" href="#${Id}">${DataNav}</a>`;
    Menu.appendChild(liElem);
}
}
NavBuilder();

and when I want to scroll the page I want to add a specific class "your-active-class" to the section which I created in my html page, and also add the class "active" to the liElem so whenever I scroll in the page and stop on a specific section the navbar should highlight the section I stopped on int the navbar it and also highlight the section itself.

the javaScript function I created to add the class active:

window.addEventListener('scroll', () => {
sections.forEach(s => {
    const topDistance = s.getBoundingClientRect().top;
    // if the distance to the top is between 0-150px
    if (topDistance >= 0 && topDistance < 150) {
        s.classList.add('your-active-class');
        document.querySelector(`<a class="menu__link" href="#${sections[s].id}"></a>`).classList.add("active");

        // otherwise, remove the class
    } else {
        s.classList.remove('your-active-class');
        document.querySelector(`<a class="menu__link" href="#${sections[s].id}"></a>`).classList.remove("active");


    }
});

});

I am still caughting this error "Uncaught TypeError: Cannot read property 'id' of undefined" in the line document.querySelector(<a class="menu__link" href="#${sections[s].id}"></a>).classList.add("active");

What is the mistake i am making that prevent me from highlighting my navbar?

the html code:

    <!DOCTYPE>
<html lang="en">

<head>
</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">

<body>
  <!-- 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 -->
      <ul id="navbar__list"></ul>
    </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 adipiscing elit. Morbi fermentum metus faucibus lectus pharetra
          dapibus. Suspendisse potenti. Aenean aliquam elementum mi, ac euismod augue. Donec eget lacinia ex. Phasellus
          imperdiet porta orci eget mollis. Sed convallis sollicitudin mauris ac tincidunt. Donec bibendum, nulla eget
          bibendum consectetur, sem nisi aliquam leo, ut pulvinar quam nunc eu augue. Pellentesque maximus imperdiet
          elit a pharetra. Duis lectus mi, aliquam in mi quis, aliquam porttitor lacus. Morbi a tincidunt felis. Sed leo
          nunc, pharetra et elementum non, faucibus vitae elit. Integer nec libero venenatis libero ultricies molestie
          semper in tellus. Sed congue et odio sed euismod.</p>

        <p>Aliquam a convallis justo. Vivamus venenatis, erat eget pulvinar gravida, ipsum lacus aliquet velit, vel
          luctus diam ipsum a diam. Cras eu tincidunt arcu, vitae rhoncus purus. Vestibulum fermentum consectetur
          porttitor. Suspendisse imperdiet porttitor tortor, eget elementum tortor mollis non.</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 adipiscing elit. Morbi fermentum metus faucibus lectus pharetra
          dapibus. Suspendisse potenti. Aenean aliquam elementum mi, ac euismod augue. Donec eget lacinia ex. Phasellus
          imperdiet porta orci eget mollis. Sed convallis sollicitudin mauris ac tincidunt. Donec bibendum, nulla eget
          bibendum consectetur, sem nisi aliquam leo, ut pulvinar quam nunc eu augue. Pellentesque maximus imperdiet
          elit a pharetra. Duis lectus mi, aliquam in mi quis, aliquam porttitor lacus. Morbi a tincidunt felis. Sed leo
          nunc, pharetra et elementum non, faucibus vitae elit. Integer nec libero venenatis libero ultricies molestie
          semper in tellus. Sed congue et odio sed euismod.</p>

        <p>Aliquam a convallis justo. Vivamus venenatis, erat eget pulvinar gravida, ipsum lacus aliquet velit, vel
          luctus diam ipsum a diam. Cras eu tincidunt arcu, vitae rhoncus purus. Vestibulum fermentum consectetur
          porttitor. Suspendisse imperdiet porttitor tortor, eget elementum tortor mollis non.</p>
      </div>
    </section>
    </main>
  <footer class="page__footer">
    <p>&copy Udacity</p>
  </footer>
  <script src="js/app.js"></script>
</body>

</html>

and the active class and your-active-class

.active {
padding: 25px;
background-color: black;
color: white;

}

section.your-active-class {
background: rgb(0, 0, 0);
background: linear-gradient(0deg, rgba(211, 39, 39, 0.356) 0%, rgba(223, 10, 10, 0.548) 100%);

}

2 Answers

querySelector expects that you pass a valid CSS selector to it, which you aren't doing. You need to change it a little bit and select by the href's value.

In a queryselector you cannot use the <a>-syntax.

document.querySelector(`a[href="#${sections[s].id}"]`)

The basic syntax of foreach loops in JS is: forEach((element) => { /* ... */ } ).

Other syntaxes:

// Arrow function
forEach((element) => { /* ... */ } )
forEach((element, index) => { /* ... */ } )
forEach((element, index, array) => { /* ... */ } )

// Callback function
forEach(callbackFn)
forEach(callbackFn, thisArg)

// Inline callback function
forEach(function(element) { /* ... */ })
forEach(function(element, index) { /* ... */ })
forEach(function(element, index, array){ /* ... */ })
forEach(function(element, index, array) { /* ... */ }, thisArg)

The variable s in your code is already the element, but not the index(key). Use directly ${s.id} directly instead.

Also, we don't use querySelector() in that way (getting the whole element by typing in their HTML in).

Your code can be replaced by:

document.querySelector(`a[href="#${s.id}"]`)

For more info on querySelector(), please visit https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

P.S. Also, I really wonder if there is really a class in your page called your-active-class, cuz it seems like you copied completely from a website :P If there is not, you can simply remove the s.classList.add('your-active-class');

Related