Skip to content

Instantly share code, notes, and snippets.

@tammyhart
Last active November 3, 2024 22:05
Show Gist options
  • Save tammyhart/eabe13624914cc8bbf0331eda51d2234 to your computer and use it in GitHub Desktop.
Save tammyhart/eabe13624914cc8bbf0331eda51d2234 to your computer and use it in GitHub Desktop.
useStickyRef.js - Add class when stuck
/* essential styles for hook to work */
header {
/*
this is set in the JS so the the primary behavior isn't dependant on the stylesheet
position: sticky;
top: -1px;
*/
}
/* styles for when header is stuck */
header.stuck {
background: rgba(0, 0, 0, 0.5);
}
/* styles for presentation purposes */
body {
background: linear-gradient(
to bottom,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.5) 100%
),
#2A3B46;
margin: 0;
font-family: sans-serif;
color: white;
}
div {
height: 300vh;
}
header {
padding: 1rem;
transition: .3s ease-out;
}
import React from "react"
import ReactDOM from "react-dom"
import useStickyRef from "./useStickyRef"
import "./App.css"
// use the hook on the header element
const App = () => {
const stickyRef = useStickyRef()
return (
<div>
<br /><br /><br /><br />
<header ref={stickyRef}>
Scroll to see me change when I'm stuck
</header>
</div>
)
}
// render the app on the #root element in the HTML
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
import { useEffect, useRef } from "react"
const useStickyRef = () => {
const ref = useRef(null)
useEffect(() => {
// https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
const observer = new IntersectionObserver(
([entry]) => {
if (ref.current) {
// toggle the class when the entry intersects in and out
ref.current.classList.toggle("stuck", entry.intersectionRatio < 1)
}
},
// execute callback when target is fully visible
{ threshold: 1.0 }
);
if (ref.current) {
// set this here so we don't have to depend on the stylesheet for the essentials
ref.current.style.position = "sticky"
// required for `position: sticky` to work
// less than 0 required for `intersectionRatio < 1` to work
ref.current.style.top = "-1px"
// observe the element
observer.observe(ref.current)
}
// stop observing on unmount
return () => {
if (ref.current) {
observer.unobserve(ref.current)
}
}
}, [ref])
return ref
}
export default useStickyRef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment