Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - check if element is visible in DOM

People also ask

How do you check if an element is visible in react?

To check if an element is in the viewport in React. js: Set the ref prop on the element. Use the IntersectionObserver API to track if the element is intersecting.

How can I tell if a DOM element is visible in the current viewport?

Summary. Use the getBoundingClientRect() method to get the size of the element and its relative position to the viewport. Compare the position of the element with the viewport height and width to check if the element is visible in the viewport or not.

How do you check if a div is visible or not?

You can use the jQuery :visible selector to check whether an element is visible in the layout or not. This selector will also select the elements with visibility: hidden; or opacity: 0; , because they preserve space in the layout even they are not visible to the eye.


Here is a reusable hook that takes advantage of the IntersectionObserver API.

The hook

export default function useOnScreen(ref) {

  const [isIntersecting, setIntersecting] = useState(false)

  const observer = new IntersectionObserver(
    ([entry]) => setIntersecting(entry.isIntersecting)
  )

  useEffect(() => {
    observer.observe(ref.current)
    // Remove the observer as soon as the component is unmounted
    return () => { observer.disconnect() }
  }, [])

  return isIntersecting
}

Usage

const DummyComponent = () => {
  
  const ref = useRef()
  const isVisible = useOnScreen(ref)
  
  return <div ref={ref}>{isVisible && `Yep, I'm on screen`}</div>
}

You can attach a ref to the element that you want to check if it is on the viewport and then have something like:

  /**
   * Check if an element is in viewport
   *
   * @param {number} [offset]
   * @returns {boolean}
   */
  isInViewport(offset = 0) {
    if (!this.yourElement) return false;
    const top = this.yourElement.getBoundingClientRect().top;
    return (top + offset) >= 0 && (top - offset) <= window.innerHeight;
  }


  render(){

     return(<div ref={(el) => this.yourElement = el}> ... </div>)

  }

You can attach listeners like onScroll and check when the element will be on the viewport.

You can also use the Intersection Observer API with a polyfil or use a HoC component that does the job


Based on Avraam's answer I wrote a Typescript-compatible small hook to satisfy the actual React code convention.

import { useRef, useEffect, useState } from "react";
import throttle from "lodash.throttle";

/**
 * Check if an element is in viewport

 * @param {number} offset - Number of pixels up to the observable element from the top
 * @param {number} throttleMilliseconds - Throttle observable listener, in ms
 */
export default function useVisibility<Element extends HTMLElement>(
  offset = 0,
  throttleMilliseconds = 100
): [Boolean, React.RefObject<Element>] {
  const [isVisible, setIsVisible] = useState(false);
  const currentElement = useRef<Element>();

  const onScroll = throttle(() => {
    if (!currentElement.current) {
      setIsVisible(false);
      return;
    }
    const top = currentElement.current.getBoundingClientRect().top;
    setIsVisible(top + offset >= 0 && top - offset <= window.innerHeight);
  }, throttleMilliseconds);

  useEffect(() => {
    document.addEventListener('scroll', onScroll, true);
    return () => document.removeEventListener('scroll', onScroll, true);
  });

  return [isVisible, currentElement];
}

Usage example:

const Example: FC = () => {
  const [ isVisible, currentElement ] = useVisibility<HTMLDivElement>(100);

  return <Spinner ref={currentElement} isVisible={isVisible} />;
};

You can find the example on Codesandbox. I hope you will find it helpful!


I have had the same problem, and, looks, I found the pretty good solution in pure react jsx, without installing any libraries.

import React, {Component} from "react";
    
    class OurReactComponent extends Component {

    //attach our function to document event listener on scrolling whole doc
    componentDidMount() {
        document.addEventListener("scroll", this.isInViewport);
    }

    //do not forget to remove it after destroyed
    componentWillUnmount() {
        document.removeEventListener("scroll", this.isInViewport);
    }

    //our function which is called anytime document is scrolling (on scrolling)
    isInViewport = () => {
        //get how much pixels left to scrolling our ReactElement
        const top = this.viewElement.getBoundingClientRect().top;

        //here we check if element top reference is on the top of viewport
        /*
        * If the value is positive then top of element is below the top of viewport
        * If the value is zero then top of element is on the top of viewport
        * If the value is negative then top of element is above the top of viewport
        * */
        if(top <= 0){
            console.log("Element is in view or above the viewport");
        }else{
            console.log("Element is outside view");
        }
    };

    render() {
        // set reference to our scrolling element
        let setRef = (el) => {
            this.viewElement = el;
        };
        return (
            // add setting function to ref attribute the element which we want to check
            <section ref={setRef}>
                {/*some code*/}
            </section>
        );
    }
}

export default OurReactComponent;

I was trying to figure out how to animate elements if the are in viewport.

Here is work project on CodeSandbox.