import { useEffect, RefObject } from "react"; import { getScrollParent, getParentNode, } from "popper.js/dist/umd/popper-utils"; export function useParentScroll( elementRef: RefObject, onScroll: (event: Event) => void ) { useEffect(() => { if (!elementRef.current || !onScroll) { return () => null; } // https://github.com/popperjs/popper-core/blob/v1.x/packages/popper/src/utils/setupEventListeners.js const dom = elementRef.current; let scrollParents: (HTMLElement | Window)[] = []; let scrollParent: HTMLElement = getScrollParent(dom); function handleScroll(event: Event) { // IE 元素内部滚动会冒泡至 window // 导致非 scrollParent 元素滚动触发 onScroll if (event.target === document || event.target === event.currentTarget) { onScroll(event); } } // Resize event listener on window window.addEventListener("resize", handleScroll, { passive: true }); while (scrollParent) { const isBody = scrollParent.nodeName === "BODY"; const target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; target.addEventListener("scroll", handleScroll, { passive: true }); scrollParents.push(target); if (isBody) { break; } scrollParent = getScrollParent(getParentNode(scrollParent)); } scrollParent = null; // Scroll event listener on scroll parents return () => { window.removeEventListener("resize", handleScroll); scrollParents.forEach(parent => parent.removeEventListener("scroll", handleScroll) ); scrollParents = null; }; }, [Boolean(onScroll), elementRef.current]); // eslint-disable-line react-hooks/exhaustive-deps }