DEV Community

Cover image for How to fade in content as it scrolls into view

How to fade in content as it scrolls into view

selbekk on August 29, 2019

Today, I want to show you a technique for displaying content in a nice and nifty way - by fading it in as it shows up! The fady slidy par...
Collapse
 
dance2die profile image
Sung M. Kim • Edited

Thank you for the post, @selbekk~

For completeness, one can unobserve the ref in FadeInSection on unmount.

  React.useEffect(() => {
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        console.log(`entry`, entry, `is = ${entry.isIntersecting}`);
        setVisible(entry.isIntersecting);
      });
    });

    const { current } = domRef;
    observer.observe(current);

    //                      πŸ‘‡ 
    return () => observer.unobserve(current);
  }, []);
Enter fullscreen mode Exit fullscreen mode

I wasn't aware of this unobserve until running into the issue recently when I implemented my sticky components using IntersectionObserver, which had a memory leak.

error

Here is the fork with unobserve & "unmount" button.

Collapse
 
selbekk profile image
selbekk • Edited

Ah that’s true - forgot about that one! I’ll update the example later today to include it (with credit given, of course)

Edit: Updated the post.

Collapse
 
dance2die profile image
Sung M. Kim

Thank you 🀜

Collapse
 
_kushagra profile image
Kushagra 🎈

To make it run only once the following works, thanks op for the amazing tutorial

function FadeInSection(props) {
        const [isVisible, setVisible] = React.useState(false);
        const domRef = React.useRef();
        React.useEffect(() => {
            const observer = new IntersectionObserver(entries => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        setVisible(entry.isIntersecting);
                    }
                });
            });
            observer.observe(domRef.current);
            return () => observer.unobserve(domRef.current);
        }, []);
        return (
            <div
                className=className={`fade-in-section ${isVisible ? 'is-visible' : ''}`}
                ref={domRef}
            >
                {props.children}
            </div>
        );
    }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
eerk profile image
eerk

Great example, especially since the so-called "simple example" on MDN is actually really complicated!

I'm still wondering why you are creating a new IntersectionObserver() for each component? In most online examples the observable elements are added to one single IntersectionObserver:

let observer = new IntersectionObserver(...)
let mythings = document.querySelectorAll('thing')
mythings.forEach(thing => {
    observer.observe(thing)
})
Collapse
 
selbekk profile image
selbekk

Hi!

I could've created a single intersection observer too, but to be honest it doesn't matter too much. If you have hundreds of things you want to fade in - sure, optimize it. I think the way I wrote it is a bit easier to understand from a beginner's point of view.

Collapse
 
victoruvarov profile image
Victor Uvarov

How can you get this to work with TypeScript? React.useRef() needs a type. Not sure what type the dom ref is. Any ideas?

Collapse
 
selbekk profile image
selbekk

HTMLDivElement?

Collapse
 
victoruvarov profile image
Victor Uvarov

Thanks I figured it out already. There were 3 problems.

  • useRef() should be initialized with null and typed.
const domRef = useRef<HTMLDivElement>(null);
  • the ref will be null for a second, so I just needed to have a local var in the first lines of the effect
const current = domRef.current;
if (!current) return;
Collapse
 
amiangie profile image
am i angie?

This is a great post, but gods I hate this effect, it makes page unsearchable when very often Ctrl + F is the quickest method to find something.

Collapse
 
selbekk profile image
selbekk

That’s true - but there are ways around that. Semantically, the content is there - so just skipping the visibility setting would enable search

Collapse
 
amiangie profile image
am i angie?

I've yet to see this implemented properly in the wild :)

Collapse
 
chrissavoie profile image
Chris Savoie

I'd encourage testing this with heatmapping tools like Hotjar. I had to eliminate something similar because it presented Hotjar from being able to screenshot a full page to lay the heatmap data over top.

Collapse
 
selbekk profile image
selbekk

That’s true - I think you could add some workarounds to make it work regardless though. If I ever end up using hoyjar, I’ll update the article with my findings

Collapse
 
ginniecodes profile image
Jhinel Arcaya

Thank you for this post!
Very useful, I didn't know about IntersectionObserver, more awesome features to take a look at.

Collapse
 
rickgove profile image
Rick Gove • Edited

How would I reverse this effect once it's about to leave the viewport?

I have it disappearing, but without any effects this way:

 .fade-out-observe {
    width: 100%;
    position: absolute;
  }



function vh() {
    var h = Math.max(
      document.documentElement.clientHeight,
      window.innerHeight || 0
    );
    return h;
  }

useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => setVisible(entry.isIntersecting));
    });
    observer.observe(fadeOutRef.current);
    return () => observer.unobserve(fadeOutRef.current);
  }, []);


 <div
        ref={fadeOutRef}
        className="fade-out-observe"
        style={{ top: vh() * 0.4 }}
      />

Enter fullscreen mode Exit fullscreen mode
Collapse
 
chrisachard profile image
Chris Achard

Huh, neat. Thanks for the post! I like the use of hooks too :)

Collapse
 
cheerupemodev profile image
Jess Rezac

Thanks for including the accessibility note!

Collapse
 
seblawrence profile image
Seb

Hi, firstly thanks for the tutorial. I was wondering if there is a way to make it so content is not faded if it is already in view.. I can't seem to resolve this, would be so grateful if someone had a solution for this.

Collapse
 
sinteticwizard profile image
Ramon Ramirez

ooosom, i gona try out, thanks you very much!

Collapse
 
evatkautz profile image
Eva Tkautz

Super useful.

Collapse
 
selbekk profile image
selbekk

Thanks!

Collapse
 
puigpuch profile image
Ra Amaral

Is it possible to adapt it so that the animation only occurs when scrolling down and not when scrolling up?

Collapse
 
cheshireoctopus profile image
Chandler Moisen

Check out this comment: dev.to/_kushagra/comment/lf24

Collapse
 
gaiagd profile image
GaiaGD

This is great! Thank you!