How to Detect When a Sticky Element Gets Pinned

By  on  

The need for position: sticky was around for years before it was implemented natively, and I can boast that I implemented it with JavaScript and scroll events for ages. Eventually we got position: sticky, and it works well from a visual perspective, but I wondered how can we determine when the element actually became pinned due to scroll.

We can determine if an element has become sticky thanks to the IntersectionObserver API!

Pinning an element to the top of its container is as easy as one CSS directive:

.myElement {
  position: sticky;
}

The question still remains about how we can detect an element being pinned. Ideally there would be a :stuck CSS directive we could use, but instead the best we can do is applying a CSS class when the element becomes sticky using a CSS trick and some JavaScript magic:

.myElement {
  position: sticky;
  /* use "top" to provide threshold for hitting top of parent */
  top: -1px;
}

.is-pinned {
  color: red;
}
const el = document.querySelector(".myElement")
const observer = new IntersectionObserver( 
  ([e]) => e.target.classList.toggle("is-pinned", e.intersectionRatio < 1),
  { threshold: [1] }
);

observer.observe(el);

As soon as myElement becomes stuck or unstuck, the is-pinned class is applied or removed. Check out this demo:

See the Pen WNwVXKx by David Walsh (@darkwing) on CodePen.

While there's not too much JavaScript involved, I hope we eventually get a CSS pseudo-class for this. Something like :sticky, :stuck, or :pinned seems as reasonable as :hover and :focus do.

Recent Features

  • By
    CSS Gradients

    With CSS border-radius, I showed you how CSS can bridge the gap between design and development by adding rounded corners to elements.  CSS gradients are another step in that direction.  Now that CSS gradients are supported in Internet Explorer 8+, Firefox, Safari, and Chrome...

  • By
    Write Better JavaScript with Promises

    You've probably heard the talk around the water cooler about how promises are the future. All of the cool kids are using them, but you don't see what makes them so special. Can't you just use a callback? What's the big deal? In this article, we'll...

Incredible Demos

Discussion

  1. Rob Simpson

    Is there anyway to have a fallback for IE11?

  2. Isabella

    So the reason this works is because the observer is tracking if the item is 100% on screen. By setting -1px on the top, the item becomes only 99.99% on screen, thus triggering the observer.

    Am I understanding that right?

  3. Jonas

    I would go for :stuck since it feels best semantically.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!