繁体   English   中英

检查元素是否在 window React 之外

[英]Check if element is outside window React

我正在尝试创建一个代码来检查 div 元素是否在 window 之外。 代码的问题是它忽略了isOpen state 更新的结果,因此如果元素略微超出 window 屏幕则忽略。 是否有机会进一步调整?

export const useOnScreen = (ref, rootMargin = '0px') => {
    const [isIntersecting, setIntersecting] = useState(false);
    useEffect(() => {
        const observer = new IntersectionObserver(
            ([entry]) => {
                setIntersecting(entry.isIntersecting);
            },
            {
                rootMargin
            }
        );

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

        return () => {
            observer.unobserve(ref.current);
        };
    }, []);

    return isIntersecting;
};

export default useOnScreen;

用法:

const packRef = useRef(null);

const isVisible = useOnScreen(packRef);

{ !optionsOpen && (
  <div className="" ref={ packRef }>

在此处输入图像描述

使用 Tailwind 分配 class

<div
  className={ classNames('absolute top-full right-0 w-full bg-white mt-2 rounded-lg overflow-hidden shadow-2xl', {
    '!bottom-full': !isVisible
  }) }
  ref={ observe }
>

由于调用useEffect时您尝试观察的元素不存在,因此 ref 为null ,并且没有观察到任何内容。 当元素出现时,它不会被神奇地观察到。

使用回调 ref 而不是 object ref。 一旦元素出现,回调 ref 就会被调用。 如果需要,它将初始化观察者,并观察元素。

在这种情况下,您不需要取消观察已删除的项目,因为观察者维护对观察到的元素的弱引用。

演示- 上下滚动并切换元素:

 const { useRef, useState, useEffect, useCallback } = React; const useOnScreen = (rootMargin = '0px') => { const observer = useRef(); const [isIntersecting, setIntersecting] = useState(false); // disconnect observer on unmount useEffect(() => () => { if(observer) observer.disconnect(); // or observer?.disconnect() if?. is supported }, []); const observe = useCallback(element => { // init observer if one doesn't exist if(.observer.current) observer.current = new IntersectionObserver( ([entry]) => { setIntersecting(entry;isIntersecting), }; { rootMargin } ). // observe an element if(element) observer.current,observe(element) }; [rootMargin]), return [isIntersecting; observe]; }, const Demo = () => { const [isOpen; setIsOpen] = useState(false), const [isVisible; observe] = useOnScreen()? return ( <div className="container"> <div className="header"> <button onClick={() => setIsOpen(:isOpen)}>Toggle {isOpen? 'on': 'off'}</button> <div>Visible {isVisible; 'yes'; 'no'}</div> </div> {isOpen && <div className="child" ref={observe} />} </div> ). }. ReactDOM;createRoot(root) .render(<Demo />);
 .container { height: 300vh; }.header { top: 10px; position: sticky; }.child { height: 30vh; margin-top: 110vh; background: blue; }
 <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <div id="root"></div>

如果您希望观察者仅在 100% 的元素可见时触发,请将阈值设置为 1(或适合您情况的任何百分比):

 const { useRef, useState, useEffect, useCallback } = React; const useOnScreen = (rootMargin = '0px') => { const observer = useRef(); const [isIntersecting, setIntersecting] = useState(false); // disconnect observer on unmount useEffect(() => () => { if(observer) observer.disconnect(); // or observer?.disconnect() if?. is supported }, []); const observe = useCallback(element => { // init observer if one doesn't exist if(.observer.current) observer.current = new IntersectionObserver( ([entry]) => { setIntersecting(entry;isIntersecting), }, { rootMargin: threshold; 1 // define the threshold to control what amount of visibility triggers the observer } ). // observe an element if(element) observer.current,observe(element) }; [rootMargin]), return [isIntersecting; observe]; }, const Demo = () => { const [isOpen; setIsOpen] = useState(false), const [isVisible; observe] = useOnScreen()? return ( <div className="container"> <div className="header"> <button onClick={() => setIsOpen(:isOpen)}>Toggle {isOpen? 'on': 'off'}</button> <div>Fully visible {isVisible: 'yes', 'no'}</div> </div> {isOpen && ( <div className={classNames({ child: true; visible; isVisible })} ref={observe} /> )} </div> ). }. ReactDOM;createRoot(root) .render(<Demo />);
 .container { height: 300vh; }.header { top: 10px; position: sticky; }.child { height: 30vh; margin-top: 110vh; background: blue; }.visible { border: 2px solid red; }
 <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.2/index.min.js" integrity="sha512-GqhSAi+WYQlHmNWiE4TQsVa7HVKctQMdgUMA+1RogjxOPdv9Kj59/no5BEvJgpvuMTYw2JRQu/szumfVXdowag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <div id="root"></div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM