0

0

如何用Resize Observer监听DOM元素尺寸变化?

幻影之瞳

幻影之瞳

发布时间:2025-09-21 17:09:01

|

800人浏览过

|

来源于php中文网

原创

Resize Observer 提供高性能、精确的DOM元素尺寸监听,相比 window.onresize 具有更优的性能、细粒度控制和避免循环触发的优势,适用于自适应组件、响应式布局等场景,并需注意兼容性处理与内存管理。

如何用resize observer监听dom元素尺寸变化?

Resize Observer
是一个非常实用的Web API,它允许我们以高性能、非侵入性的方式,实时监听并响应DOM元素内容区域(content-box)尺寸的变化。这就像给一个元素装上了一个灵敏的传感器,一旦它的宽或高发生改变,我们就能立即得到通知,从而执行相应的逻辑,而无需依赖传统的
window.onresize
全局事件或耗费性能的轮询。

解决方案

使用

Resize Observer
的核心流程其实挺直观的,我个人觉得它比
Mutation Observer
好理解多了。

首先,你需要创建一个

ResizeObserver
实例,并传入一个回调函数。这个回调函数会在被监听元素的尺寸发生变化时被触发。

// 1. 创建 ResizeObserver 实例
const myObserver = new ResizeObserver(entries => {
  // entries 是一个 ResizeObserverEntry 对象的数组
  // 每一个 entry 都代表一个被监听元素的尺寸变化
  for (let entry of entries) {
    // entry.target 是发生尺寸变化的 DOM 元素
    // entry.contentRect 提供了该元素新的 content-box 尺寸(DOMRectReadOnly 对象)
    // entry.contentBoxSize 和 entry.borderBoxSize 提供了更详细的尺寸信息(ResizeObserverSize 数组)
    // 它们提供了更精确的 width 和 height,考虑了设备的像素比

    const { width, height } = entry.contentRect; // 常用
    // 或者更现代的写法,获取最新的 content-box 尺寸
    // const latestContentBoxSize = entry.contentBoxSize[0]; // 数组,通常只有一个元素
    // const width = latestContentBoxSize.inlineSize;
    // const height = latestContentBoxSize.blockSize;

    console.log(`元素 ${entry.target.id || entry.target.tagName} 的新尺寸是:${width}x${height}`);

    // 在这里执行你希望在尺寸变化时触发的逻辑
    // 例如:重新绘制图表、调整组件布局、更新文本溢出状态等
    if (entry.target.id === 'myChartContainer') {
      updateChart(entry.target, width, height);
    }
  }
});

// 2. 选择你要监听的 DOM 元素
const chartContainer = document.getElementById('myChartContainer');
const sidebar = document.getElementById('sidebar');

// 3. 开始监听这些元素
if (chartContainer) {
  myObserver.observe(chartContainer);
}
if (sidebar) {
  myObserver.observe(sidebar);
}

// 4. 当不再需要监听时,记得取消监听,防止内存泄漏
// 例如,在一个组件销毁时调用:
// myObserver.unobserve(chartContainer); // 取消监听单个元素
// myObserver.disconnect(); // 取消监听所有元素,并停止观察者

这个过程的核心就是:创建观察者 -> 指定目标 -> 开始观察 -> (可选)停止观察。回调函数里的

entries
数组尤其关键,它让你能知道是哪个元素发生了变化,以及变化后的具体尺寸。

Resize Observer与传统方法(如window.onresize)相比,有哪些显著优势?

说实话,我刚开始接触前端时,遇到需要监听元素尺寸变化的需求,第一反应总是

window.onresize
,然后就是一堆手动计算和性能担忧。但
Resize Observer
的出现,简直是解决这类问题的利器,它和传统方法比起来,优势简直不要太多。

首先,最核心的优势就是性能和粒度

window.onresize
监听的是整个浏览器窗口的尺寸变化,这意味着即使你只关心页面中某个小组件的尺寸,每次用户调整浏览器窗口大小,这个全局事件都会被触发。这不仅会造成不必要的计算,还可能导致页面卡顿,尤其是在回调函数中执行了复杂DOM操作时。而
Resize Observer
则完全不同,它只监听你明确指定的DOM元素,并且浏览器对它的触发机制做了大量优化,它会在每次布局和渲染之后异步触发,通常与
requestAnimationFrame
同步,这意味着它不会阻塞主线程,而且能有效避免“布局抖动”问题。

其次,是精确性和便捷性

window.onresize
只能告诉你视口变了,至于你目标元素的尺寸,你得自己通过
getBoundingClientRect()
或者
offsetWidth
/
offsetHeight
去获取,这本身就是额外的计算。
Resize Observer
的回调直接就给你提供了
ResizeObserverEntry
对象,里面包含了
contentRect
contentBoxSize
borderBoxSize
等详细的尺寸信息。你甚至可以知道是
content-box
还是
border-box
发生了变化,这对于需要精确控制布局的场景来说,简直是福音。我记得有一次我写一个响应式图表,就是因为
Resize Observer
提供了精确的
content-box
尺寸,才让我省去了不少手动计算 padding 和 border 的麻烦。

还有一个非常重要的点,就是它避免了潜在的无限循环和循环引用问题。一个常见的误区是,在

window.onresize
的回调里改变了某个元素的尺寸,这可能会再次触发
window.onresize
,如果处理不当,就可能陷入无限循环。但
Resize Observer
在设计上就考虑到了这一点,即使你在回调中改变了被监听元素的尺寸,它也不会在同一帧内再次触发,有效防止了这种“自激”现象。这让我能更放心地在回调中进行布局调整,而不用担心副作用。

在实际项目中,Resize Observer有哪些常见的应用场景和潜在的挑战?

在我多年的开发经验里,

Resize Observer
已经成了我工具箱里不可或缺的一部分。它的应用场景非常广泛,我个人觉得主要集中在需要动态响应内部元素尺寸变化的场景。

常见的应用场景:

  1. 自适应组件或图表库: 这是最典型的应用。比如你有一个复杂的D3.js或Echarts图表,它需要根据父容器的尺寸变化来重新渲染或调整内部布局。用
    Resize Observer
    监听图表容器,一旦尺寸变了,就调用图表的
    resize()
    方法,完美!我经常用它来做仪表盘上的各种小部件,它们的大小可能因为侧边栏的展开或折叠而改变。
  2. 响应式布局和容器查询: 虽然CSS的
    @media
    查询主要针对视口,但很多时候我们希望组件能根据其父容器的尺寸变化来调整样式,这被称为“容器查询”。在原生CSS的容器查询还未普及或不满足需求时,
    Resize Observer
    就是一个很好的替代方案。我曾用它来根据父容器宽度,动态切换子元素的排列方式或字体大小。
  3. 虚拟列表和无限滚动: 当列表容器的高度变化时,我们可能需要动态计算可见区域能容纳多少个列表项,从而优化渲染性能。
    Resize Observer
    就能监听这个容器的高度,及时更新虚拟列表的渲染范围。
  4. 文本溢出处理: 某些场景下,我们可能希望文本在特定容器内显示,如果溢出就显示省略号,或者动态调整字体大小。
    Resize Observer
    可以帮助我们监听容器尺寸,然后判断文本是否溢出,并执行相应的处理。
  5. 拖拽/缩放组件: 如果你的应用允许用户拖拽或缩放某个UI组件(例如一个可调整大小的面板),
    Resize Observer
    可以监听这个组件的尺寸变化,然后更新其内部内容或通知其他依赖组件。

潜在的挑战:

  1. 兼容性问题: 这是老生常谈了,虽然现代浏览器支持度很好,但IE系列是完全不支持的。这意味着在需要兼容旧版浏览器的项目中,你可能需要引入 Polyfill。
  2. 回调触发时机和性能考量: 尽管
    Resize Observer
    自身性能优秀,但如果你的回调函数中执行了非常耗时或大量的DOM操作,仍然可能导致性能问题。理解它是在布局和渲染之后异步触发的,有助于你更好地安排回调内的逻辑,比如将复杂的DOM修改放到
    requestAnimationFrame
    中。
  3. 内存管理: 忘记
    unobserve()
    disconnect()
    是一个常见的坑,尤其是在单页应用(SPA)中。当组件被销毁时,如果其内部创建的
    Resize Observer
    没有被清理,它会继续监听已经不存在的DOM元素,导致内存泄漏。我常常会遇到这种情况,然后得花点时间去排查。
  4. 嵌套监听的复杂性: 如果你同时监听了父元素和子元素,当父元素尺寸变化时,子元素的尺寸也可能随之变化,这会导致多个
    Resize Observer
    回调被触发。虽然这通常不是问题,但在某些复杂场景下,可能会让调试变得稍微复杂一点。

如何处理Resize Observer的兼容性问题,以及在回调中如何避免性能陷阱?

处理

Resize Observer
的兼容性和优化其性能,是把它用好、用稳的关键。

兼容性解决方案:

云从科技AI开放平台
云从科技AI开放平台

云从AI开放平台

下载

最直接有效的方式就是使用 Polyfill。市面上有一些成熟的

Resize Observer
Polyfill 库,比如
github.com/que-etc/resize-observer-polyfill

  1. 安装 Polyfill: 如果你使用npm或yarn,可以这样安装:

    npm install resize-observer-polyfill
    # 或者
    yarn add resize-observer-polyfill
  2. 引入和使用: 在你的应用入口文件或需要使用

    Resize Observer
    的地方引入它。通常,你只需要在全局环境下确保
    window.ResizeObserver
    存在即可。

    import ResizeObserver from 'resize-observer-polyfill';
    
    // 确保全局的 ResizeObserver 可用
    if (!window.ResizeObserver) {
      window.ResizeObserver = ResizeObserver;
    }
    
    // 现在你就可以放心地使用 new ResizeObserver(...) 了

    当然,你也可以在条件判断后,只在不支持的浏览器中加载这个 Polyfill,实现按需加载,进一步优化性能。

  3. 特性检测: 在实际使用

    new ResizeObserver()
    之前,最好进行一个简单的特性检测,虽然引入 Polyfill 后这步可能显得不那么必要,但它仍是一个良好的编程习惯。

    if (typeof window.ResizeObserver !== 'undefined') {
      // 使用 ResizeObserver
    } else {
      // 提供降级方案或使用 Polyfill
    }

在回调中避免性能陷阱:

尽管

Resize Observer
本身是高性能的,但回调函数中的操作仍然可能成为瓶颈。

  1. 保持回调函数轻量化: 回调函数的核心职责应该是获取最新的尺寸信息,并触发必要的更新。避免在回调中执行大量、复杂的DOM操作或耗时的计算。如果确实需要执行这些操作,可以考虑将它们延迟。

  2. 利用

    requestAnimationFrame
    优化DOM操作: 如果回调中需要修改DOM,为了避免布局抖动和提高渲染效率,我通常会把这些DOM操作包裹在
    requestAnimationFrame
    中。这样可以确保你的DOM修改在浏览器下一次重绘之前统一执行,减少不必要的强制回流和重绘。

    const myObserver = new ResizeObserver(entries => {
      for (let entry of entries) {
        // 获取尺寸信息
        const { width, height } = entry.contentRect;
    
        // 将复杂的DOM操作或渲染逻辑放入 requestAnimationFrame
        window.requestAnimationFrame(() => {
          // 例如,更新一个 canvas 的尺寸并重新绘制
          if (entry.target.id === 'myChartCanvas') {
            const canvas = entry.target;
            canvas.width = width;
            canvas.height = height;
            drawChart(canvas, width, height);
          }
        });
      }
    });
  3. 合理使用防抖(Debounce)或节流(Throttle):

    Resize Observer
    内部已经有了一些防抖机制,它会在每一帧的末尾批量处理所有观察到的变化。但在某些极端情况下,如果你的回调函数确实非常耗时,并且你对响应的实时性要求不是那么高,你仍然可以在回调内部手动添加防抖或节流。但请注意,这可能会引入额外的延迟,所以要慎重评估。我个人经验是,大部分情况下
    Resize Observer
    自身的优化已经足够,很少需要手动加防抖。

  4. 及时清理观察者: 这是我之前提过的,但真的非常重要。当被监听的元素从DOM中移除,或者包含

    Resize Observer
    的组件被销毁时,务必调用
    observer.unobserve(element)
    来取消对特定元素的监听,或者调用
    observer.disconnect()
    来停止观察所有元素。这能有效防止内存泄漏,确保应用长期运行的稳定性。

    // 在组件卸载或元素被移除时调用
    myObserver.unobserve(chartContainer); // 针对特定元素
    // 或者
    // myObserver.disconnect(); // 如果这个观察者只服务于这个组件,并且组件要被销载了

通过这些方法,你就能在项目中更稳健、更高效地利用

Resize Observer
,为用户带来更流畅的交互体验。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
ECharts是什么
ECharts是什么

ECharts是基于JavaScript的开源可视化库,能够帮助开发者轻松地实现各种复杂的数据可视化效果,提供了丰富的图表类型和交互功能。本专题为大家提供ECharts是什么的相关的文章、下载、课程内容,供大家免费下载体验。

281

2023.08.04

echarts自适应大小设置
echarts自适应大小设置

使用ECharts的自适应大小设置可以使图表能够根据不同屏幕尺寸和设备进行自适应。一种是使用resize事件,在图表容器大小改变时重新渲染图表;另一种是使用CSS样式,通过设置图表容器的宽度和高度为百分比值,使图表容器根据父元素的大小进行自适应。根据实际需求选择合适的方法,可以使图表在不同设备上都能够良好地显示和交互 。

485

2023.09.13

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

443

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

605

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

531

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

576

2023.07.28

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

1

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.9万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

CSS教程
CSS教程

共754课时 | 42.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号