Reputation: 357
I want to create a div, which shows blurr on the bottom of the div (when overflow exists). The issue is that the div contains props.children, which also consist of other components / html tags inside.
I tried using ref (React.useRef), but the ref is set even before the children are renderen. I tried using useEffect with the ref, but still when I use for example element.current.scrollbarHeight I get 0.
How can I check if the rendered div (with children) has overflow? (I am using a functional component)
<div> {props.children} </div>
For generating the blurr I am using styled-components.
Upvotes: 2
Views: 3502
Reputation: 120498
It's not possible to know if arbitrary children are fully rendered without each one offering specialized callbacks. As such, it might be better to watch elements for size changes.
We can use ResizeObserver
to our advantage here.
If we create a nested pair of div:
<div class="fixed_size_overflow_auto">
<div>
{children}
</div>
</div>
making the outer div have fixed dimensions, we can listen for resize on the inner div. When we detect those resizes, we can calculate if the outer div has overflowed by comparing its offsetHeight
to its scrollHeight
.
Here is the idea, wrapped up in a component that can be styled using styled-components
:
const DetectContentResizeElement: FC<{
className?: string;
onContentResized?: (v: { hasOverflow: boolean }) => void;
}> = ({ children, onContentResized, className }) => {
const contentRef = useRef<HTMLDivElement>(null);
const outerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!contentRef.current || !outerRef.current) {
return;
}
const obs = new ResizeObserver(() => {
const hasOverflow =
(outerRef.current?.offsetHeight ?? 0) <
(contentRef.current?.offsetHeight ?? 0);
onContentResized?.({ hasOverflow });
});
obs.observe(contentRef.current);
return () => obs.disconnect();
}, [onContentResized]);
return (
<div className={className} ref={outerRef}>
<div ref={contentRef}>{children}</div>
</div>
);
};
You can fix the size of this component by "subclassing" it with styled-components:
const FixedHeight = styled(DetectContentResizeElement)`
height: 200px;
width: 200px;
border: solid 1px black;
overflow: auto;
`;
so now you can use this container and get events that tell you when it overflowed:
<FixedHeight
onContentResized={({ hasOverflow }) =>
console.log(`resized. hasOverflow: ${hasOverflow}`)
}
>
<SomeChildElement />
</FixedHeight>
See it in action in this codesandbox project (uses resize-observer-polyfill ponyfill for best compatibility)
Upvotes: 3