Search code examples
javascriptintersection-observer

Detect when scrolled below element


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.


Solution

  • 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.