React - check if element is visible in DOM

前端 未结 5 2031
刺人心
刺人心 2020-12-25 12:24

I\'m building a form - series of questions (radio buttons) the user needs to answer before he can move on to the next screen. For fields validation I\'m using yup (npm packa

5条回答
  •  一整个雨季
    2020-12-25 12:42

    Answer based on the post from @Alex Gusev

    React hook to check whether the element is visible with a few fixes and based on the rxjs library.

    import React, { useEffect, createRef, useState } from 'react';
    import { Subject, Subscription } from 'rxjs';
    import { debounceTime, throttleTime } from 'rxjs/operators';
    
    /**
     * 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
     * @param {boolean} triggerOnce - Trigger renderer only once when element become visible
     */
    export default function useVisibleOnScreen(
      offset = 0,
      throttleMilliseconds = 1000,
      triggerOnce = false,
      scrollElementId = ''
    ): [boolean, React.RefObject] {
      const [isVisible, setIsVisible] = useState(false);
      const currentElement = createRef();
    
      useEffect(() => {
        let subscription: Subscription | null = null;
        let onScrollHandler: (() => void) | null = null;
        const scrollElement = scrollElementId
          ? document.getElementById(scrollElementId)
          : window;
        const ref = currentElement.current;
        if (ref && scrollElement) {
          const subject = new Subject();
          subscription = subject
            .pipe(throttleTime(throttleMilliseconds))
            .subscribe(() => {
              if (!ref) {
                if (!triggerOnce) {
                  setIsVisible(false);
                }
                return;
              }
    
              const top = ref.getBoundingClientRect().top;
              const visible =
                top + offset >= 0 && top - offset <= window.innerHeight;
              if (triggerOnce) {
                if (visible) {
                  setIsVisible(visible);
                }
              } else {
                setIsVisible(visible);
              }
            });
          onScrollHandler = () => {
            subject.next();
          };
          if (scrollElement) {
            scrollElement.addEventListener('scroll', onScrollHandler, false);
          }
          // Check when just loaded:
          onScrollHandler();
        } else {
          console.log('Ref or scroll element cannot be found.');
        }
    
        return () => {
          if (onScrollHandler && scrollElement) {
            scrollElement.removeEventListener('scroll', onScrollHandler, false);
          }
          if (subscription) {
            subscription.unsubscribe();
          }
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [offset, throttleMilliseconds, triggerOnce, scrollElementId]);
    
      return [isVisible, currentElement];
    }
    

提交回复
热议问题