How I added a neat effect in 30 lines using IntersectionObserver

Ciprian on Friday, March 12, 2021 in JavaScript DOM

This is something I’ve always wanted to add to my theme, but I was afraid of the impact on the Core Web Vitals. As you scroll down, specific elements fade and slide into view. The more discrete, the better.

Without further ado, here’s the (vanilla) JavaScript you need to add. It’s based on the IntersectionObserver API and it only works in modern browsers.
No IE 11 or Legacy Edge for you, and perhaps no Opera or older Safari.

First, we check if at least one element exists. Then we set some options and decide on the threshold (the percentage of the target element which is visible). Then, we check if the element is visible – according with the params set in options – then add a .is-changed class to the element, otherwise, remove it.

IntersectionObserver Options

To better understand the threshold and rootMargin check out below visualization demo created by Michelle Barker. The dotted black border specifies the rootMargin and the two light color boxes at top and bottom specify the threshold value. As you scroll the page, which elements are in view will be displayed in the left section.

See the Pen IntersectionObserver Visualizer by Michelle Barker (@michellebarker) on CodePen.

🖱️ Here’s another tutorial on how to use the IntersectionObserver API to mark current scroll progress.

Keeping the .is-changed class will trigger the animation once. If you scroll back up and then down again, the animation won’t be triggered. If you want it to be triggered again, although I find it a bit confusing and uneasy to the eye – uncomment the line below in the else block:'is-changed');

and comment the line below:


The JavaScript

document.addEventListener('DOMContentLoaded', () => {
     * Observe and animate all elements having a class of .saturn-anim-in
    if (document.querySelector('.saturn-anim-in')) {
        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.isIntersecting) {
                } else {

        // 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);

        document.querySelectorAll('.saturn-anim-in').forEach(box => { observer.observe(box) });
}, false);


Then we have the CSS. Note that you can add any effect you want, even multiple effects:

.saturn-anim-in {
    transform: translateY(4rem) translateZ(0);
    transition: transform cubic-bezier(0.19, 1, 0.22, 1) 2s, opacity cubic-bezier(0.19, 1, 0.22, 1) 2s;
    opacity: 0.5;
} {
    transform: translateY(0rem) translateZ(0);
    opacity: 1;

A nice improvement here would be to fade and slide in the paragraphs, and fade and slide from the left the headings. Also, fade the images. Basically, add more classes, such as .saturn-anim-left or .saturn-fade-in and then reference them in the code:

document.querySelectorAll('.saturn-anim-in').forEach(box => { observer.observe(box) });
document.querySelectorAll('.saturn-anim-left').forEach(box => { observer.observe(box) });
document.querySelectorAll('.saturn-fade-in').forEach(box => { observer.observe(box) });

This depends on the page structure, though, and the amount of animation and movement it adds. It’s very subjective.

Here’s an example of this animation behaviour. Scroll down to see how the headings and paragraphs are slowly fading in and sliding up into view.

Related posts

2 comments on “How I added a neat effect in 30 lines using IntersectionObserver

  1. I am getting an error:
    Fatal error: Uncaught TypeError: array_map(): Argument #1 ($callback) must be a valid callback, function “whiskey_add_quotes” not found or invalid function name

Leave a Reply

Your email address will not be published. Required fields are marked *