Sidebar links not changing when the chapter titles are reaching the top on scroll, they change earlier

Tags: , , ,



I have a sidebar with anchor links that point to a specific content part when I click on them.

The problem appears when I’m scrolling the content, the link in the sidebar gets highlighted when the section title barely appears at the bottom.

How can I make the link change when the title of chapter content reaches the top of sidebar?

Here’s what I tried so far but isn’t working exactly as I want.

// Sticky sidebar scroll
        let stickyLinks = document.getElementById("scrollspy");
        let mainNavLinks = document.querySelectorAll("#scrollspy ul li a");

        window.addEventListener("scroll", event => {
            let fromTop = window.scrollY;
      //start from a spe
            if(fromTop >= 100){
                stickyLinks.classList.add("sticky");
            }
            else {
                stickyLinks.classList.remove("sticky");
      }
      // add link gray background
      mainNavLinks.forEach(link => {
                let section = document.querySelector(link.hash);

                if (section.offsetTop <= fromTop && section.offsetTop + section.offsetHeight > fromTop) {
                    link.classList.add("current");
                } else {
                    link.classList.remove("current");
                }
            });
    });
h2 {
font-size: 18px;
}
p {
font-size: 12px;
}
container {
width: 75%;
}
.footer {
  background-color: #eee;
  text-align: center;
  height: 150px;
}
#scrollspy ul {
    list-style-type: none;
    padding: 0;
    border: 1px solid #eee;
    border-radius: 0.25rem;
}

#scrollspy ul li a {
    text-decoration: none;
    padding: 0.5rem 1rem;
    display: block;
}
#scrollspy ul li:first-child {
    color: #444;
    font-size: 0.9rem;
    font-weight: bold;
    padding: 1rem;
}
#scrollspy ul li a.current {
    background: #eee;
  }
  
.sticky {
    position: fixed;
    top: 10%;
    width: 16%;
    background-color: white;
  z-index: 1;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<div class="container">
<!--some header on top-->
<div class="row startContent">
    <div class="col-sm-3">
        <h4>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h4>
    </div>
    <div class="col-sm-9">
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
    </div>
</div>
<!-- content and sidebar -->
<div class="row">
    <div class="col-sm-3">
      <div id="scrollspy">
        <ul>
            <li>Contents</li>
            <li><a href="#chapter1">Link 1</a></li>
            <li><a href="#chapter2">Link 2</a></li>
            <li><a href="#chapter3">Link 3</a></li>
            <li><a href="#chapter4">Link 4</a></li>
            <li><a href="#chapter5">Link 5</a></li>
        </ul>
      </div>
    </div>
    <div class="col-sm-9">
        <div id="chapter1">
            <h2>Title of Chapter 1</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
        </div> <!--End of chapter 0-->
        <div id="chapter2">
            <h2>Title of Chapter 2</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
        </div> <!--End of chapter 0-->
        <div id="chapter3">
            <h2>Title of Chapter 3</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
        </div> <!--End of chapter 0-->
        <div id="chapter4">
            <h2>Title of Chapter 4</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
        </div> <!--End of chapter 0-->
        <div id="chapter5">
            <h2>Title of Chapter 5</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
        </div> <!--End of chapter 0-->
    </div>
</div>
</div>

Answer

The reason this is happening is because section.offsetTop is returning the position of the section relative to the offset parent. In this case the parent is the div that contains the sections <div class="col-sm-9">.

To fix the problem, we just need to adjust the value for the top of each section to take into account the distance of the parent from the top. We do this as follows:

1. Add an id to the parent container to make it easier to select, e.g.

<div id="chapters-container" class="col-sm-9">

2. Get the offsetTop of the parent so we can add this to the top of the section:

let chaptersTop = document.getElementById("chapters-container").offsetTop;

3. Calculate the actual top position in our scroll listener by adding the distance from the top of the parent container to the position of the section within the parent, which gives us its position from the top of the body in this case. Then we use this when we are checking how far the page is scrolled:

sectionTop = section.offsetTop + chaptersTop;
if (sectionTop <= fromTop && (sectionTop + section.offsetHeight) > fromTop) {
    //...
}

Working Example:
(FYI, you aren’t closing your li elements in your code, I fixed that here)

// Sticky sidebar scroll
let stickyLinks = document.getElementById("scrollspy");
let mainNavLinks = document.querySelectorAll("#scrollspy ul li a");
let chaptersTop = document.getElementById("chapters-container").offsetTop;


window.addEventListener("scroll", event => {
  let fromTop = window.scrollY;

  if (fromTop >= 100) {
    stickyLinks.classList.add("sticky");
  } else {
    stickyLinks.classList.remove("sticky");
  }
  // add link gray background
  mainNavLinks.forEach(link => {
    let section = document.querySelector(link.hash);

    sectionTop = section.offsetTop + chaptersTop;
    if (sectionTop <= fromTop && (sectionTop + section.offsetHeight) > fromTop) {
      link.classList.add("current");
    } else {
      link.classList.remove("current");
    }
  });
});
h2 {
  font-size: 18px;
}

p {
  font-size: 12px;
}

container {
  width: 75%;
}

.footer {
  background-color: #eee;
  text-align: center;
  height: 150px;
}

#scrollspy ul {
  list-style-type: none;
  padding: 0;
  border: 1px solid #eee;
  border-radius: 0.25rem;
}

#scrollspy ul li a {
  text-decoration: none;
  padding: 0.5rem 1rem;
  display: block;
}

#scrollspy ul li:first-child {
  color: #444;
  font-size: 0.9rem;
  font-weight: bold;
  padding: 1rem;
}

#scrollspy ul li a.current {
  background: #eee;
}

.sticky {
  position: fixed;
  top: 10%;
  width: 16%;
  background-color: white;
  z-index: 1;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<div class="container">
  <!--some header on top-->
  <div class="row startContent">
    <div class="col-sm-3">
      <h4>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</h4>
    </div>
    <div class="col-sm-9">
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat. Praesent
        tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna consectetur eleifend.
        Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
    </div>
  </div>
  <!-- content and sidebar -->
  <div class="row">
    <div class="col-sm-3">
      <div id="scrollspy">
        <ul>
          <li>Contents</li>
          <li><a href="#chapter1">Link 1</a>
          </li>
          <li><a href="#chapter2">Link 2</a>
          </li>
          <li><a href="#chapter3">Link 3</a>
          </li>
          <li><a href="#chapter4">Link 4</a>
          </li>
          <li><a href="#chapter5">Link 5</a>
          </li>
        </ul>
      </div>
    </div>
    <div class="col-sm-9" id="chapters-container">
      <div id="chapter1">
        <h2>Title of Chapter 1
          <h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat.
              Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna
              consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
      </div>
      <!--End of chapter 0-->
      <div id="chapter2">
        <h2>Title of Chapter 2
          <h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat.
              Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna
              consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
      </div>
      <!--End of chapter 0-->
      <div id="chapter3">
        <h2>Title of Chapter 3
          <h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat.
              Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna
              consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
      </div>
      <!--End of chapter 0-->
      <div id="chapter4">
        <h2>Title of Chapter 4
          <h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat.
              Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna
              consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
      </div>
      <!--End of chapter 0-->
      <div id="chapter5">
        <h2>Title of Chapter 5
          <h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ultricies, lacus eu viverra euismod, arcu dolor condimentum augue, vel congue est ante sit amet purus. Quisque non nunc ut urna gravida porta. Duis sodales vitae tortor in feugiat.
              Praesent tellus ante, pharetra sed mauris at, eleifend semper nisl. Donec condimentum purus eget viverra euismod. Ut felis ipsum, sagittis sodales faucibus suscipit, tempus a ante. Integer ac dignissim libero. In tincidunt purus at urna
              consectetur eleifend. Nullam hendrerit nulla et enim auctor rhoncus. Pellentesque interdum augue nec augue pulvinar fermentum. Phasellus imperdiet elit id arcu vehicula, ac commodo mauris hendrerit.</p>
      </div>
      <!--End of chapter 0-->
    </div>
  </div>
</div>


Source: stackoverflow