2

I would like to detect a user's scroll and make an element fixed to top by adding a class when it's reached by scolling and below. The class should be removed when the user is scrolling above the element. I can't use the css property position: sticky.

I'm currently using IntersectionObserver but it's adding the fixed class even when the element is not in view because of !entry.isIntersecting. Is there a way to get around that? Is there another or better way to add a fixed class only when scrolled below an element?

  const watcher = document.querySelector('.watcher');
  const evu = document.getElementById('box');

  const createObserver = () => {
    const options = {
      root: null,
      trackVisibility: true,
      delay: 100,
      threshold: [.9]
    }

    const handler = (entries) => {
      entries.forEach((entry) => {
        entry.target.nextElementSibling.classList.toggle('fixed', !entry.isIntersecting);
      })
    }

    if ('IntersectionObserver' in window) {
      const observer = new window.IntersectionObserver(handler, options);
      observer.observe(watcher);

    }
  };

Fiddle here.

Meek
  • 3,086
  • 9
  • 38
  • 64

1 Answers1

1

There are definitely a few ways to do this, but using your code the simplest is to just add a check to see if the current scroll position window.scrollY is greater than or equal to the y position of your element entry.boundingClientRect.top.

const watcher = document.querySelector('.watcher');
  const evu = document.getElementById('box');

  const createObserver = () => {
    const options = {
      root: null,
      trackVisibility: true,
      delay: 100,
      threshold: [.9]
    }

    const handler = (entries) => {
      entries.forEach((entry) => {
        entry.target.nextElementSibling.classList.toggle('fixed', !entry.isIntersecting && window.scrollY >= entry.boundingClientRect.top);
      })
    }

    if ('IntersectionObserver' in window) {
      const observer = new window.IntersectionObserver(handler, options);
      observer.observe(watcher);

    }
  };

  window.addEventListener('load', () => {
    createObserver();
  }, false);

  window.addEventListener('scroll', () => {
    createObserver();
  }, {
    passive: true
  });
.fixed {
  position: fixed;
  top: 0;
  border: 1px solid red;
}
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

<div class="watcher"></div>
<div id="element">
  Add fixed class only when I'm intersected or below.
  Remove class when above element.
</div>

<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>

NOTE

You definitely could do this without an intersection observer by attaching an event listener to the scroll event of the window object and checking if the window's y position is greater than or equal to your specific element's top position.

EssXTee
  • 1,783
  • 1
  • 13
  • 18