Tracking an Elements Visibility in React
Recently on a React project I had a need to know when an element came into view and how much was visible. I was able to find several partial solutions, even an NPM package that came close.
function useIsInViewport(ref) {
const [isIntersecting, setIsIntersecting] = useState(false);
const observer = useMemo(
() =>
new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
}
),
[],
);
useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, observer]);
return isIntersecting;
}
IntersectionObserver does provide the intersectionRatio in its callback so let’s make an update to get that as well.
function useIsInViewport(ref) {
const [isIntersecting, setIsIntersecting] = useState(false);
const [intersectionRatio, setIntersectionRatio] = useState(0);
const observer = useMemo(
() =>
new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
}
),
[],
);
useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, observer]);
return [isIntersecting, intersectionRatio];
}
function useIsInViewport(ref) {
const [isIntersecting, setIsIntersecting] = useState(false);
const [intersectionRatio, setIntersectionRatio] = useState(0);
const observer = useMemo(
() =>
new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
},
{ threshold: [0.5] }
),
[],
);
useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, observer]);
return [isIntersecting, intersectionRatio];
}
I know the dimensions of the document and I can get the dimensions of the viewport so I can calculate what the intersection ratio is that would constitute 50% of the viewport available but in our most recent iteration we will only get an update when it reaches 50%. If the viewport is such that at max, you will only get 40% then we will never reach our threshold
function useIsInViewport(ref) {
const [isIntersecting, setIsIntersecting] = useState(false);
const [intersectionRatio, setIntersectionRatio] = useState(0);
const observer = useMemo(
() =>
new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
},
{ threshold: [0.1, 0.2, 0.3, 0.4, 0.5] }
),
[],
);
useEffect(() => {
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref, observer]);
return [isIntersecting, intersectionRatio];
}
Conclusion
As you may have noticed earlier the threshold option on IntersectionObserver is an array so the way I solved this problem was to pass in some thresholds that are granular enough for me to answer my visibility problem without excessive updates. If you need more precision, you add more thresholds to a higher precision level, and if not fewer thresholds or more broad values.
About Intertech
Intertech is a Software Development Consulting Firm that provides single and multiple turnkey software development teams, available on your schedule and configured to achieve success as defined by your requirements independently or in co-development with your team. Intertech teams combine proven full-stack, DevOps, Agile-experienced lead consultants with Delivery Management, User Experience, Software Development, and QA experts in Business Process Automation (BPA), Microservices, Client- and Server-Side Web Frameworks of multiple technologies, Custom Portal and Dashboard development, Cloud Integration and Migration (Azure and AWS), and so much more. Each Intertech employee leads with the soft skills necessary to explain complex concepts to stakeholders and team members alike and makes your business more efficient, your data more valuable, and your team better. In addition, Intertech is a trusted partner of more than 4000 satisfied customers and has a 99.70% “would recommend” rating.