You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义React Hook在Windows浏览器正常但Safari失效问题排查

ResizeObserver在Safari中检测元素溢出失效的问题分析与修复

问题描述

基于ResizeObserver实现的React自定义HookuseDetectOverflow,在Windows平台的Chrome、Edge、Firefox中能正常检测HTMLDivElement的垂直溢出,但在macOS和iOS的Safari里完全失效——就算元素明显溢出,isOverflowing始终返回false,控制台还没任何报错。

相关代码

import { useState, useEffect, RefObject } from 'react';

// Custom hook to detect overflow
const useDetectOverflow = (bool: boolean, htmlRef: RefObject<HTMLDivElement>) => {
  const [isOverflowing, setIsOverflowing] = useState(false);

  useEffect(() => {
    const deployOverflowObserver = () => {
      const observer = new ResizeObserver(() => {
        if (htmlRef.current) {
          setIsOverflowing(htmlRef.current.scrollHeight > htmlRef.current.clientHeight);
        }
      });

      if (htmlRef.current) {
        observer.observe(htmlRef.current);
      }
    };

    const timeoutId = setTimeout(deployOverflowObserver, 250);

    return () => {
      clearTimeout(timeoutId);
    };
  }, [bool, htmlRef]);

  const scrollToBottom = () => {
    if (htmlRef.current) {
      htmlRef.current.scrollTo({
        top: htmlRef.current.scrollHeight,
        behavior: 'smooth',
      });
    }
  };

  return { isOverflowing, scrollToBottom };
};

export default useDetectOverflow;

原因分析

  • ResizeObserver触发时机差异:Safari里ResizeObserver的触发时机比Chrome等浏览器晚,你加的250ms延迟可能赶不上Safari完成元素布局计算的时间,导致初始化时scrollHeight和clientHeight的数值还没更新到正确状态。
  • Safari对scrollHeight的计算特性:当元素设置了overflow: hidden或overflow-y: auto时,Safari计算scrollHeight可能会忽略一些排版细节,或者在内容动态更新后,需要等待重排完成才能拿到准确值。
  • 未清理ResizeObserver实例:当前代码只清理了定时器,没在组件卸载或依赖更新时停止观察者,这会在Safari里留下残留的观察者,干扰后续的数值计算。

修复方案

1. 移除不必要延迟,确保观察者及时初始化

删掉250ms的setTimeout,直接在useEffect里初始化ResizeObserver,用requestAnimationFrame确保布局完成后再计算:

useEffect(() => {
  let observer: ResizeObserver | null = null;

  if (htmlRef.current) {
    observer = new ResizeObserver(() => {
      if (htmlRef.current) {
        requestAnimationFrame(() => {
          setIsOverflowing(htmlRef.current!.scrollHeight > htmlRef.current!.clientHeight);
        });
      }
    });
    observer.observe(htmlRef.current);
  }

  return () => {
    observer?.disconnect(); // 清理观察者
  };
}, [bool, htmlRef]);

2. 改用offsetHeight替代clientHeight做对比

Safari里offsetHeight包含边框和内边距,计算溢出时比clientHeight更稳定:

setIsOverflowing(htmlRef.current.scrollHeight > htmlRef.current.offsetHeight);

3. 补充监听scroll事件

Safari中ResizeObserver可能不会在内容变化时可靠触发,加个scroll事件监听作为补充:

useEffect(() => {
  let observer: ResizeObserver | null = null;
  const updateOverflowStatus = () => {
    if (htmlRef.current) {
      setIsOverflowing(htmlRef.current.scrollHeight > htmlRef.current.offsetHeight);
    }
  };

  if (htmlRef.current) {
    observer = new ResizeObserver(updateOverflowStatus);
    observer.observe(htmlRef.current);
    htmlRef.current.addEventListener('scroll', updateOverflowStatus);
  }

  return () => {
    observer?.disconnect();
    htmlRef.current?.removeEventListener('scroll', updateOverflowStatus);
  };
}, [bool, htmlRef]);

内容的提问来源于stack exchange,提问作者Richardson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 14:39:50