Skip to content

Resize Observer

概要

ResizeObserver 用于监听元素尺寸变化。不同于 window.resize 只监听视口变化,ResizeObserver 可以监听任意 DOM 元素的宽高变化。

一、基本用法

js
const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const { width, height } = entry.contentRect
    console.log(`元素:`, entry.target)
    console.log(`新尺寸: ${width}px × ${height}px`)
    
    // contentBoxSize 更精确(支持 sub-pixel)
    const boxSize = entry.contentBoxSize[0]
    console.log(`精确: ${boxSize.inlineSize}px × ${boxSize.blockSize}px`)
  })
})

observer.observe(document.querySelector('.resizable'))

二、实战场景

1. 响应式组件(替代 Container Queries 的降级方案)

js
const cardObserver = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const width = entry.contentRect.width
    const card = entry.target
    
    if (width < 300) {
      card.classList.add('card--compact')
    } else if (width > 600) {
      card.classList.add('card--wide')
    } else {
      card.classList.remove('card--compact', 'card--wide')
    }
  })
})

document.querySelectorAll('.card-wrapper').forEach(el => {
  cardObserver.observe(el)
})

2. 自适应文本截断

js
function autoTruncate(element: HTMLElement) {
  const fullText = element.dataset.fullText || element.textContent
  element.dataset.fullText = fullText!

  const observer = new ResizeObserver(() => {
    const availableHeight = element.clientHeight
    element.textContent = fullText
    
    // 逐字减少直到适合容器
    while (element.scrollHeight > availableHeight && element.textContent!.length > 3) {
      element.textContent = element.textContent!.slice(0, -4) + '...'
    }
  })

  observer.observe(element)
}

3. 保持 iframe 高度自适应

js
// 嵌入的 iframe 内容高度变化时自动调整
const iframe = document.querySelector('iframe')

const ro = new ResizeObserver((entries) => {
  for (const entry of entries) {
    // iframe 内 body 高度变化 → 调整 iframe 高度
    iframe.style.height = entry.contentRect.height + 'px'
  }
})

// 注意:需要 iframe 同源才能访问内部 DOM
iframe.onload = () => {
  ro.observe(iframe.contentDocument!.body)
}

4. 图表自适应重绘

ts
import * as echarts from 'echarts'

function useResponsiveChart(container: HTMLElement) {
  const chart = echarts.init(container)

  const observer = new ResizeObserver(() => {
    chart.resize()  // 容器尺寸变化时图表自动适配
  })
  observer.observe(container)

  return () => {
    observer.disconnect()
    chart.dispose()
  }
}

5. 检测滚动条出现/消失

js
const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const hasScrollbar = entry.target.scrollHeight > entry.target.clientHeight
    // 滚动条出现 → 调整 padding 避免内容跳动
    entry.target.style.paddingRight = hasScrollbar ? '17px' : '0'
  })
})

observer.observe(document.body)

三、与 window.resize 对比

特性ResizeObserverwindow.resize
监听对象任意 DOM 元素仅浏览器视口
触发时机元素尺寸变化视口变化
精度sub-pixel (contentBoxSize)整数像素
性能✅ 浏览器原生优化✅ 但也有节流
常用场景组件级响应、图表适配全局布局切换

四、注意点

循环限制

如果回调中修改了被观察元素的尺寸,会触发无限循环。ResizeObserver 会抛出 ResizeObserver loop limit exceeded 错误并停止。应避免在回调中改变同一元素的尺寸。

js
// ❌ 危险:回调节器中修改同一元素尺寸
const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    entry.target.style.width = '100px'  // 会触发新的回调!
  })
})

// ✅ 正确:用条件判断避免循环
const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const currentWidth = entry.contentRect.width
    if (Math.abs(currentWidth - targetWidth) > 1) {
      // 只在差异超过 1px 时才调整
      entry.target.style.width = targetWidth + 'px'
    }
  })
})

小结

ResizeObserver 是监听任意 DOM 元素尺寸变化的标准 API,常用于 响应式组件图表自适应文本截断 等场景。使用时需注意避免在回调中修改同一元素尺寸,防止触发无限循环。与 window.resize 相比,ResizeObserver 支持精确到 sub-pixel 级别的尺寸监听,适用于组件级响应式设计。

CC BY-NC-SA 4.0 协议