How to check if an element is visible in the viewport using JavaScript and React Hook.
Tags: javascript, react, frontend, programming
Published: 2024-02-24 18:35:22

In web development, it's common to determine if an element is currently visible within the viewport, especially when implementing features like lazy loading, animations triggered by scrolling, or analytics tracking. Fortunately, JavaScript provides methods to accomplish it efficiently. Below, we'll explore two approaches to achieve this.

#### Method 1: Using `getBoundingClientRect()`

The size of an element and its position relative to the viewport are returned by the `getBoundingClientRect()` method. We can leverage this to check if an element is within the viewport.

The below code demonstrates how to implement a simple element visibility checker in a React component using `getBoundingClientRect()`.

```Javascript
import React, { useRef, useEffect, useState } from 'react';

const ElementVisibilityChecker = () => {
  const elementRef = useRef(null);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const handleScroll = () => {
      if (elementRef.current) {
        const rect = elementRef.current.getBoundingClientRect();
        const isVisible = (
          rect.top >= 0 &&
          rect.left >= 0 &&
          rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
          rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
        setIsVisible(isVisible);
      }
    };

    window.addEventListener('scroll', handleScroll);
    // Initial check on component mount
    handleScroll();

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div>
      <div
        ref={elementRef}
        style={{
          width: '100px',
          height: '100px',
          backgroundColor: isVisible ? 'green' : 'red',
          margin: '50px',
        }}
      >
        {isVisible ? 'Visible' : 'Not Visible'}
      </div>
      <p>Scroll to see visibility changes!</p>
    </div>
  );
};

export default ElementVisibilityChecker;

```
Inside the `handleScroll` function, we use `getBoundingClientRect()` to obtain the position and dimensions of the element relative to the viewport. Based on these dimensions, we determine whether the element is currently visible in the viewport or not. We update the state variable isVisible accordingly.

**Initial Check:** We call `handleScroll()` initially when the component mounts to check the visibility status of the element.

**Cleanup:** We remove the event listener when the component unmounts to prevent memory leaks.

**Rendering:** We render a `<div>` element with a ref pointing to elementRef. The background color of this div changes based on the isVisible state, allowing us to visually observe the visibility changes. We also provide a message indicating whether the element is visible or not.

#### Method 2: Using Intersection Observer API (Modern Approach)

The Intersection Observer API provides a more performant way to observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport. This component demonstrates how to implement a simple element `sibility` checker using the Intersection Observer API in a React component.

```Javascript
import React, { useRef, useEffect, useState } from 'react';

const ElementVisibilityChecker = () => {
  const [isVisible, setIsVisible] = useState(false);
  const targetRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        setIsVisible(entry.isIntersecting);
      },
      {
        root: null, // viewport
        rootMargin: '0px', // no margin
        threshold: 0.5, // 50% of target visible
      }
    );

    if (targetRef.current) {
      observer.observe(targetRef.current);
    }

    // Clean up the observer
    return () => {
      if (targetRef.current) {
        observer.unobserve(targetRef.current);
      }
    };
  }, []);

  return (
    <div>
      <div
        ref={targetRef}
        style={{
          width: '100px',
          height: '100px',
          backgroundColor: isVisible ? 'green' : 'red',
          margin: '50px',
        }}
      >
        {isVisible ? 'Visible' : 'Not Visible'}
      </div>
      <p>Scroll to see visibility changes!</p>
    </div>
  );
};

export default ElementVisibilityChecker;
```

**Observer Configuration:** We configure the Intersection Observer with options such as root, rootMargin, and threshold. In this example, we set root to null (viewport), rootMargin to 0px (no margin), and threshold to 0.5 (50% of the target visible).

**Observer Callback:** The callback function receives an array of entries, each representing a target element's visibility. We update the isVisible state based on `entry.isIntersecting`.

**Observer Registration:** We use the observation method to start observing the target element's visibility. We pass the current value of targetRef.current as the target element.

**Cleanup:** We use the unobserved method to stop observing the target element's visibility when the component unmounts.

**Rendering:** We render a `<div>` element with a ref pointing to targetRef.The background color of this div changes based on the `isVisible` state, allowing us to visually observe the visibility changes. We also provide a message indicating whether the element is visible or not.

**Summary**
The article explores techniques to monitor element visibility in React. It discusses using `getBoundingClientRect()` for basic checks and highlights the advantages of the Intersection Observer API for efficient and flexible visibility tracking. The Intersection Observer API is recommended for its superior performance and customization options, making it ideal for complex scenarios in React applications.

**Reference:**
- [getBoundingClientRect() method](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect)

- [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)

- [React hook check if the element is in viewport](https://gist.github.com/codler/7237990baef87ef00b9e1f7989c24ee5)