Reputation: 6378
I am changing the background color of a table cell when it is fully visible. To accomplish this task I have used an intersection observer.
All the code is available on code sandbox with demo reproducing the bug:
I am using useInView hook for the intersection observer:
export const useInView = options => {
const ref = useRef();
const [isVisible, setIsVisible] = useState(false);
const [intersectionRatio, setIntersectionRatio] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
console.log("called");
setIsVisible(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
}, options);
if (ref.current) observer.observe(ref.current);
return () => {
if (ref.current) observer.unobserve(ref.current);
};
}, [ref, options]);
return [ref, isVisible, intersectionRatio];
};
Here is the table in which I am rendering the data:
<div id="my-table" style={{ height: 200, width: 200, overflow: "auto" }}>
<table>
<tbody>
{tableValues.map((row, rowIndex) => (
<tr key={rowIndex}>
{row.map((cell, cellIndex) => (
<CellRendererContainer
key={`${rowIndex}${cellIndex}`}
rowIndex={rowIndex}
cellIndex={cellIndex}
tableCell={cell}
/>
))}
</tr>
))}
</tbody>
</table>
</div>
The intersection observer is used in CellRenderer, which is divided into two files:
CellRendererContainer.js
const CellRendererContainer = ({ rowIndex, cellIndex, tableCell }) => {
const [ref, isVisible, intersectionRatio] = useInView({
root: document.querySelector("#my-table"),
rootMargin: "0px",
threshold: 0.0
});
return (
<CellRenderer
ref={ref}
isVisible={isVisible}
intersectionRatio={intersectionRatio}
rowIndex={rowIndex}
cellIndex={cellIndex}
tableCell={tableCell}
/>
);
};
And here is the actual rendering of the cell, CellRenderer.js
const CellRenderer = React.forwardRef(
({ isVisible, intersectionRatio, rowIndex, cellIndex, tableCell }, ref) => (
<td
ref={ref}
style={{
padding: 25,
backgroundColor:
rowIndex > 0 && cellIndex > 0 && isVisible && intersectionRatio > 0.9
? "red"
: "white"
}}
>
{tableCell}
</td>
)
);
The problem in the current implementation is that: intersection observer's callback for some items is not being called.
Expected result:
Any cell that becomes visible should have a red background as soon as it is visible fully. Otherwise, that cell should be white.
Code sandbox link: (so that you does not need to scroll to the top)
Upvotes: 5
Views: 4962
Reputation: 16885
The IntersectionObserver
constructor signature is:
var observer = new IntersectionObserver(callback[, options]);
The options
argument is optional and, if supplied, should be an object with properties that describe how you'd like the newly created IntersectionObserver
to behave.
In hook.js
, you have this line:
const observer = new IntersectionObserver(([entry]) => {
console.log("called");
setIsVisible(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
}, options);
Your variable options
isn't set to something that would be useful in this context.
Instead, something like this does what you're looking for:
const observer = new IntersectionObserver(([entry]) => {
console.log("called");
setIsVisible(entry.isIntersecting);
setIntersectionRatio(entry.intersectionRatio);
}, {
threshold: 0.9
});
After this change, the event would be triggered when the relevant elements become more or less than 90% visible.
Upvotes: 6