Galanthus
Galanthus

Reputation: 2280

Scrolling too fast messing up scroll top and bottom values using IntersectionObserver

Can someone please explain to me why in the hell is the IntersectionObserver so weird and confusing? You set the values but once you scroll too fast or too slow will result in wrong results.

I have a fixed logo which when you scroll and enter another section I should console log the current section ID.

When I scroll too slow.. it works, when I scroll faster It works 50% when I scroll very fast it breaks the positions.

each section is 100VH. if you scroll from TOP and the FIXED logo enters the second div I want to show a console log of that current section AND in in reverse, it is not working because it will only show a console when you hit the top of that section.

Codepen: https://codepen.io/Merdzanovich/full/RwKByVe

What I am trying to do when the fixed LOGO bar enters gets inside the next section it should console log that current section id. but If you scroll back to the previous section and enter from BOTTOM it should console log the current

Hopefully, this is clear enough.

(() => {

  const sections = document.querySelectorAll('section');

  const changeNav = (entries) => {
    entries.forEach((entry) => {
      const id = entry.target.getAttribute('id');
      const currentY = entry.boundingClientRect.y;
      const sectionTheme = entry.target.dataset.sectionColor;
      const currentRatio = entry.intersectionRatio;
      const isIntersecting = entry.isIntersecting;
      const isAbove = currentY < entry.rootBounds.y;

      if (isIntersecting && currentRatio) {
        console.log(`DOWN: ${id}`);
      }
    });
  };

  const options = {
    threshold: [1, 0],
    rootMargin: '0px -50px'
  };

  const observer = new IntersectionObserver(changeNav, options);

  sections.forEach((section) => {
    observer.observe(section);
  });
})();
section {
  min-height: 100vh;
  position: relative;
  display: flex;
  align-items: center;
  justify-items: center;
  border: 2px solid red;
}

.fixed-bar {
  position: fixed;
}
<div class="fixed-bar">
  <h2 class="section--title">Content</h2>
</div>

<section id="intro" data-section-title="Intro">Content</section>
<section id="about" data-section-title="About">Content</section>
<section id="services" data-section-title="Services">Content</section>
<section id="contact" data-section-title="Contact">Content</section>

Upvotes: 2

Views: 2579

Answers (1)

L.Blondy
L.Blondy

Reputation: 468

The rootMargin is applied to the root element following the same rule as the CSS margin. That means that rootMargin: 0px - 50px applies a margin-left and margin-right of 0px, and a margin-top and margin-bottom of 50px. In your case I think you want the contrary

The root of the IntersectionObserver is the viewport that has a height of 100vh, if your sections also has a height of 100vh, you will hardly get the observer to trigger for the threshold: 1. As the Intersection observer does not get triggered immediately, as soon as a bit of the section gets off screen from any of its sides, the intersection observer will not get triggered, thats why you have issues when you scroll from the bottom.

The first thing to change is therefore the rootMargin

const options ={
    rootMargin:'-50px 0px 200px 0px',
    ...
} 

note that the margin bottom is 200px in order to allow the viewport to contain the section and trigger at threshold of 1

Then, the Intersection sometimes does not trigger on very slow scrolling with a threshold of 0 or 1, I tend to set a very small threshold instead:

const options ={
    rootMargin:'-50px 0px 200px 0px',
    threshold:[0.01, 0.99]
} 

with these options you should be good to go

keep in mimd that the sections should not be bigger than 100vh + 200px /*margin bottom*/ - 50px /* margin top*/ Otherwise omce again the threshold 1 would never work. If that's the case you might use a different unit for the margim bottom.

Upvotes: 4

Related Questions