DEV Community

Cover image for Detecting the Visibility of a DOM Element in React
Sandra Lewis
Sandra Lewis

Posted on

Detecting the Visibility of a DOM Element in React

It is often important to track the user's activity on a website for analytical reasons. Thanks to JavaScript's Intersection Observer API, detecting the visibility of an element in a website has now become easier.

The Intersection Observer API observes an element and detects when it is visible in the viewport, and in turn, triggers a callback function.

In this article, we will walk through an example of how we can create a useIntersection Custom Hook in React. This hook detects when a particular section of a page is viewed by the user for more than 3 seconds.

Check out the demo below.

Basic Usage

We will first create an isVisible state inside the useIntersection hook and set its initial value to false.

useIntersection.js

const useIntersection = () => {
    const [isVisible, setIsVisible] = useState(false);

    return isVisible;
}
Enter fullscreen mode Exit fullscreen mode

To use the Intersection Observer API, we need to create an observer in the useEffect hook.

useIntersection.js

import { useState, useEffect } from "react";

const useIntersection = (element, rootMargin = "0px") => {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() => {
        let currentElement;

        const observer = new IntersectionObserver(
        ([{ isIntersecting }]) => {}, { rootMargin });

        if (element?.current) {
            currentElement = element.current;
            observer.observe(currentElement);
        }

        return () => {
            observer.unobserve(currentElement);
        };
    }, [element, rootMargin]);

    return isVisible;
}

export default useIntersection;
Enter fullscreen mode Exit fullscreen mode

The Intersection API takes two parameters:

  • The callback function
  • Observer's options

The observer watches the target element and the callback function has an isIntersecting boolean value. The rootMargin is one of the options of the Intersection API. The rootMargin parameter is the minimum number of pixels which when visible in the viewport would change the isIntersecting value.

In this scenario, since the rootMargin is 0px, the isIntersecting value changes to true as soon as the currentElement is visible in the viewport.

To capture the isIntersecting state after 3 seconds, we need to create a timer and set the isVisible state to the isIntersecting value as shown below.

useIntersection.js

const useIntersection = (element, delay = 3000, rootMargin = "0px") => {
    const [isVisible, setIsVisible] = useState(false);

    useEffect(()  =>  {
        let currentElement, timer;

        const observer = new IntersectionObserver(
          [{ isIntersecting }])  =>  {
            if (timer && !isIntersecting) clearTimeout(timer);

            timer = setTimeout(()  =>  { 
                        setIsVisible(isIntersecting); 
                    }, delay);
        },
        { rootMargin });

        if (element?.current) {
            currentElement = element.current;
            observer.observe(currentElement);
        }

        return () => {
            clearTimeout(timer);
            observer.unobserve(currentElement);
        };
       }, [element, rootMargin]);

    return isVisible;
}

export default useIntersection;
Enter fullscreen mode Exit fullscreen mode

Calling the useIntersection Hook

The useIntersection hook has been used in App.js as shown below.

App.js

import { useEffect,  useRef } from "react";
import useIntersection from "./customHook/useIntersection";

export default function App() {
    const sectionFourRef = useRef(null);
    const sectionIsInViewport  = useIntersection(sectionFourRef);

    useEffect(() => {
        if (sectionIsInViewport && sectionFourRef?.current)  {
            const sectionText = sectionFourRef.current.textContent;
            window.alert(`${sectionText} is visible in the viewport!`);
        }
    },  [sectionIsInViewport]);

return  (
    <>
    <header>Header</header>
    <section>Section-1</section>
    <section>Section-2</section>
    <section>Section-3</section>
    <section ref={sectionFourRef}>Section-4</section>
    <section>Section-5</section>
    <footer>Footer</footer>
    </>
);}
Enter fullscreen mode Exit fullscreen mode

In the above code, we created a ref called sectionFourRef and passed it to the useIntersection custom hook. The hook watches for the Section-4 element in the page and displays an alert box every time the user views the section for more than 3 seconds.

Top comments (0)