页头多高不该猜:ResizeObserver 实测回写 CSS 变量

导航换行、字体晚到、内容长度变化都会让元素高度和 CSS 里的常量对不上。ResizeObserver 观察元素本身,把实测值写回变量,比监听 resize 可靠。

页头多高?导航换不换行?目录占多少宽?这些数 CSS 里能写个大概,但写不准。而且它们会变:字体加载完、窗口缩放、内容长度变化。

window 的 resize 事件不够用

window.addEventListener('resize') 只覆盖一种变化:视口尺寸。它漏掉的更多 —— 字体晚到导致的行高变化、details 展开、同一次 resize 里异步加载进来的内容。更糟的是它在每次拖动时都会触发,回调里读 getBoundingClientRect() 会和写样式交错,制造强制同步布局。

观察元素本身

ResizeObserver 观察的是元素,任何一个尺寸变化都会回调,与「为什么变」无关:

const header = document.querySelector('[data-site-header]');
if (header) {
  const observer = new ResizeObserver(() => {
    const height = Math.round(header.getBoundingClientRect().height);
    document.documentElement.style.setProperty('--anchor-offset', height + 16 + 'px');
  });
  observer.observe(header);
}

观察者回调里只写变量,不再读别的元素 —— 一旦在回调里改了会影响被观察元素尺寸的东西,浏览器就会抛出 ResizeObserver loop completed with undelivered notifications,那是在告诉你「你制造了循环」。

两个务实的细节

  • 四舍五入再写:亚像素高度在每次回调里抖动,会让 --anchor-offset 每秒变几十次,锚点补偿跟着抖。取整后同值不再写。
  • 不支持的浏览器直接跳过:这里所有用途都是补偿性质,没有它页面照样能用。不做 polyfill,也不在缺失时报错。

什么时候不该用它

如果某个尺寸能在 CSS 里直接表达(height: auto、aspect-ratio、clamp()、grid 的自动分配),就不要用 JS 去量再写回来 —— 那是在用命令式代码模仿 CSS 已经会做的事,还会多出一次布局往返。ResizeObserver 的位置是跨元素的、CSS 表达不了的关系:页头高度决定锚点偏移,内容宽度决定导轨目录要不要出现。

能量出来的就别猜;CSS 能算出来的就别量。

← 返回文章列表

评论

…