最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Intersection Observer is not trustable? - Stack Overflow

programmeradmin2浏览0评论

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
Add a ment  | 

1 Answer 1

Reset to default 6 +50

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 bee more or less than 90% visible.

发布评论

评论列表(0)

  1. 暂无评论