Some 10 days ago I made it update measurements live. Needed to make this working for a client project 👇
Scrollable overlay highlight
It’s not perfect, but that highlight is a hole punched through a darkened canvas. It uses
useDimensionsfor positioning and needs to update when you scroll.
Works good enough.
Built with hooks, live updating looks like this:
useLayoutEffectto run our code synchronously on DOM updates. Inside, we check that the
nodeexists, then create a debounced
Measure runs on
requestAnimationFrameso we don’t kill the browser even if the code falls into an infinite loop. You might get small subpixel resizes forever because of layouting edge cases. I’ve seen it happen. It’s bad.
setDimensionsis the state setter from a
useStatehook. You can see full code here.
scrolllisteners will re-run our
measure()function when things change. Scrolling will change its position, resizing might change its dimensions.
There’s a problem here 👉 if the container resizes and the window doesn’t, we’ll have stale dimensions.
IntersectionObserver to the rescue!
Last night I decided to fix that with the new IntersectionObserver API
After some research I realized I was wrong. IntersectionObserver doesn’t do what I thought it does, but it’s super neat anyway.
You want to use the
IntersectionObserver API when you have two DOM nodes and you want to know when their intersection changes. It’s all in the name right?
A common use-case is something like my react-lazyload-fadein library where you want to lazy load images juuuuust before they become visible. You could create a new intersection observer between the document and your placeholder node and wait for notifications.
Super neat, not what’s going to fix our resize edge case.
ResizeObserver, that’s the thing 👌
ResizeObserver is an even newer API. Mozilla calls it “experimental”. Support is lacking, but there are polyfills you can use.
I used this one
The idea here is that you can create a new observer object, give it a DOM node to observe and it’s going to call your function when something changes. You can give it multiple nodes too.
Something like this 👇
ResizeObserverin an effect post initial mount. Give it a method that runs when the observer fires, make it observe your React element, return the
disconnectfunction to clean up on unmount.
Perfect. Much cleaner than the hacky solution 👌
Except it doesn’t work. You can see it in the stream around the 29 minute mark. ResizeObserver was going crazy and firing as fast as we could read.
Remember when I said you can run into subtle layouting edge cases where things are unstable and resize very often? That.
So we gave up, reverted back to
window.addEventListener, and left the edge case alone. Pretend like none of this ever happened.
But it was fun to experiment 🙂
Learned something new? Want to improve your skills?
Join over 10,000 engineers just like you already improving their skills!
Here's how it works 👇
PS: You should also follow me on twitter 👉 here.
It's where I go to shoot the shit about programming.