I am changing the background color of a table cell when it is fully visible. To acplish 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 bees 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)
I am changing the background color of a table cell when it is fully visible. To acplish 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 bees 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)
Share Improve this question edited Nov 30, 2019 at 9:11 Yushin 1,7503 gold badges21 silver badges37 bronze badges asked Nov 26, 2019 at 15:16 VishalVishal 6,37814 gold badges89 silver badges164 bronze badges 2-
I poked around enough to see that the events are getting fired correctly but your code doesn't seem to always update the color correctly in response to the event. (Judicious use of
console.log
can help with debugging.) – Ouroborus Commented Nov 26, 2019 at 17:02 - @Ouroborus Thanks for verifying that. I will debug the rendering and color corrections. – Vishal Commented Nov 26, 2019 at 20:03
1 Answer
Reset to default 6 +50The 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 bee more or less than 90% visible.