动态检测文本溢出:实现一个优雅的 React Hook

468 阅读2分钟

动态检测文本溢出:实现一个优雅的 React Hook

需求场景与实现价值

在前端开发中,我们经常需要处理文本溢出场景。当容器空间不足时,通过 CSS 的 text-overflow: ellipsis 实现文本截断是常见做法。但在某些交互场景中,我们还需要:

  1. 动态感知截断状态(如显示「更多」按钮)
  2. 响应式布局中的智能提示
  3. 动态内容加载后的状态检测

本文将实现一个 React 自定义 Hook useEllipsis,帮助开发者优雅地解决这些问题。

核心检测原理

基础检测逻辑

通过比较元素的滚动尺寸与实际渲染尺寸判断溢出状态:

const isHorizontalOverflow = element.scrollWidth > element.clientWidth
const isVerticalOverflow = element.scrollHeight > element.clientHeight

智能监听机制

使用 ResizeObserver 实现精准监听:

const observer = new ResizeObserver(() => {
  // 重新计算溢出状态
})

完整实现代码

function useEllipsis(domRef: React.RefObject<HTMLElement>): boolean {
  const [isEllipsis, setIsEllipsis] = useState(false)

  useEffect(() => {
    const element = domRef.current
    if (!element)
      return

    // 核心检测方法
    const checkEllipsis = () => {
      const hasHorizontal = element.scrollWidth > element.clientWidth
      const hasVertical = element.scrollHeight > element.clientHeight
      return hasHorizontal || hasVertical
    }

    // 更新状态(防抖处理)
    const update = () => {
      const newState = checkEllipsis()
      if (newState !== isEllipsis) {
        setIsEllipsis(newState)
      }
    }

    // 双保险监听器
    const resizeObserver = new ResizeObserver(update)
    const mutationObserver = new MutationObserver(update)

    // 启动监听
    resizeObserver.observe(element)
    mutationObserver.observe(element, {
      characterData: true, // 监听文本变化
      subtree: true, // 监听所有子节点
      childList: true, // 监听子节点增删
    })

    // 手动触发一次初始检测
    update()

    // 清理函数
    return () => {
      resizeObserver.disconnect()
      mutationObserver.disconnect()
    }
  }, [domRef, isEllipsis]) // 依赖 isEllipsis 用于状态比对

  return isEllipsis
}

export default useEllipsis

核心特性解析

1. 复合条件检测

同时验证样式属性与尺寸关系:

// 验证是否应用了省略号相关样式
const isEllipsisStyle = 
  style.textOverflow === 'ellipsis' || 
  style.webkitLineClamp !== 'none'

// 验证是否实际发生溢出
const isContentOverflow =
  element.scrollWidth > element.clientWidth ||
  element.scrollHeight > element.clientHeight

2. 智能方向检测

同时支持水平和垂直方向的检测:

  • 水平方向:适用于 white-space: nowrap 场景
  • 垂直方向:适配 -webkit-line-clamp 多行截断

3. 性能优化策略

  • 自动清理观察器
  • 防抖动检测机制(示例代码可扩展)
  • 精确依赖项控制

应用示例

基础使用场景

const ArticleCard = ({ title }) => {
  const titleRef = useRef(null)
  const hasEllipsis = useEllipsis(titleRef)

  return (
    <div className="card">
      <h3 
        ref={titleRef}
        className="truncate-text" 
      >
        {title}
      </h3>
      {hasEllipsis && (
        <Tooltip content={title}>
          <Icon name="expand" />
        </Tooltip>
      )}
    </div>
  )
}

总结与展望

通过这个 Hook 的实现,我们不仅解决了文本溢出检测的技术问题,更展示了现代浏览器 API 与 React 生态结合的最佳实践。