javascripthtmlcsssticky

How to find distance between top of element and top of viewable webpage


I have an element that has position sticky and has the top property set to 30px. I want it so that when the element becomes stuck (so to say) It has a class of stuck added to it. I'm aware it can be done with intersectionObserver however I would like to know a way that I could do it in vanilla HTML, CSS or JS.

The way I have thought of so far is to add the class once the elements distance from the top of the viewable webpage is 30px. When researching, I have come across code like this:

var elDistanceToTop = window.pageYOffset + el.getBoundingClientRect().top

Although this does do something, it doesn't do want I want. What it does is it finds the top of the element to the top of the window height.

(Now at the time of writing this I am not sure if the window height is what it is called but I mean the bit of the webpage that you would get to if you scrolled up until you couldn't any more. Sometimes it is out of sight).

Using this code, the value I get is always constant as the element is not moving up or down the page, it is only the viewport is moving down the page as you scroll down.

Instead, the value that the function (or whatever) returns should change depending on where you have scrolled to. Before the 'stuck' position activates, the distance from the top should vary depending on where you scroll. But once you have hit its 'stuck' threshold the distance from the top should stay the same.

I don't mind the solution as long as it uses HTML, CSS or JS but nothing else.


Solution

  • Compare the el.getBoundingClientRect().top of the element in relation to the amount of scrollY the window has moved, then add/remove class accordingly.

    If this is not what you're looking for let know.

    const el = document.querySelector('#container'); 
    const elRectTop = el.getBoundingClientRect().top;
    
    const setStickClass = () => {
      if(window.scrollY - 30 >= elRectTop){
        el.classList.add('stuck');
      }else if(window.scrollY - 30 < elRectTop){
        el.classList.remove('stuck');
      }
    }
    
    addEventListener('scroll', setStickClass)
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }
    
    body {
      height: 300vh;
      width: 100%;
      padding-top: 50vh;
    }
    
    #container {
      position: sticky;
      top: 30px;
      outline: 2px solid black;
      height: 100vh;
      width: 100%;
    }
    
    .stuck {
      background-color: red;
    }
    <div id="container"></div>