Animating elements into view as a user scrolls down the page has long been a popular pattern, and for good reason - with minimal effort it can breathe a bit of life into an otherwise fairly static experience.
The only problem with this, historically, has been triggering these animations efficiently. The two most common methods for as long as I can remember have been listening to the document scroll
event and, slightly more recently, hooking into the requestAnimationFrame
API. These both have their uses, but as methods which run hundreds of times a second, there are inevitably performance implications. This might be close to imperceptible to the average user but that little bit of jank introduced, has always irritated me.
Enter, IntersectionObserver
. This API allows you to define an element, define an observer and then trigger some arbitrary code when your conditions are met. The real beauty is that this code is fired only when it needs to - massively improving scroll performance and completely eliminating jank.
Here's the code I use to handle the subtle fade/translate animation on this website (note from future Fraser - I no longer use this code on this website following a re-design, but this is still a perfectly acceptable technique):
function handleIntersection(entries, observer){
entries.forEach((e) => {
if(e.isIntersecting){
e.target.classList.add('intersected');
}
});
}
const observer = new IntersectionObserver(handleIntersection, { threshold: 0 });
const elementsToObserve = document.querySelectorAll('.hideonload');
elementsToObserve.forEach((el) => {
observer.observe(el);
});
Browser support
Unfortunately, as someone who still believes in supporting IE11 (at least in client projects) IntersectionObserver is only supported in Edge onwards, so remember to polyfill if this logic is somehow business critical or, my preference, feature detect and deactivate the functionality entirely.