Trigger animations with Intersection Observer API

on 11th November 2019

(Last updated 5th July 2023)

There are already a few excellent articles and examples about the Intersection Observer API on CSS Tricks or Codepen, but I want to give you a little snippet on how to animate elements when they enter the visible viewport.

It’s pretty common to see a website where things happen on scroll, be it used for animations, lazy-loading images, infinite scrolling, etc. At Liquid Light we normally use a library like ScrollReveal to achieve those things, but these can be replaced with this API, since this is now finally supported in all major browsers.

Here's a simple example that shows circles fading-in/out when they become partially visible in the viewport. The JavaScript uses to API to add and remove a CSS class of isVisible to the element when it is 50% visible.

See the Pen Trigger animation on scroll with IntersectionObserver API by Liquid Light (@liquidlight) on CodePen.

<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

Code

This section contains the code used in the example, in case the CodePen demo isn’t working.

HTML
<h1>Scroll down</h1>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
CSS
:root {
	--base: #202050;
	--border: #ccc;
}

body {
	display: flex;
	align-items: center;
	flex-direction: column;
	margin-top: 10rem;
}

.box {
	width: 30vh;
	height: 30vh;
	margin: 20vh 0;
	border: 0.2rem solid var(--border);
	border-radius: 50%;
	transition: background 0.8s ease,
		border 0.4s ease;
}

.box.isVisible {
	border-color: var(--base);
	background-color: var(--base);
}
JS
const options = {
	root: null, // use the document's viewport as the container
	rootMargin: '0px', // % or px - offsets added to each side of the intersection 
	threshold: 0.5 // percentage of the target element which is visible
}

let callback = (entries) => { 
	entries.forEach(entry => {
		
		// If entry (box) is visible - according with the params set in `options`
		// then adds `isVisible` class to box
		// otherwise removes `isVisible` class
		if(entry.isIntersecting) {
			entry.target.classList.add('isVisible');
		} else {
			entry.target.classList.remove('isVisible');		
		}

	});
}

// Create the intersection observer instance by calling its constructor and passing it a
// callback function to be run whenever a threshold is crossed in one direction or the other:
let observer = new IntersectionObserver(callback, options);

// Get all the `.box` from DOM and attach the observer to these
document.querySelectorAll('.box')
	.forEach(box => { observer.observe(box) });

This article was posted in Development