JavaScript 面试题
目录

请解释 Resize Observer API 的作用和用法。如何使用 Resize Observer API 监听元素的尺寸变化?

· 约 6 分钟读完 · 第 199 / 507 章

推荐答案

Resize Observer API 用于监听 HTML 元素的尺寸变化。当元素的大小(包括内容盒、内边距盒、边框盒)发生变化时,它会异步地通知观察者。这使得我们能够响应元素的尺寸变化,从而进行布局调整、动态计算等操作,而且不会像使用 window.onresize 那样触发频繁的回流和重绘。

使用 ResizeObserver 的基本步骤如下:

  1. 创建 ResizeObserver 实例:

    回调函数接收一个 entries 数组,其中每个 entry 都是一个 ResizeObserverEntry 对象,包含了观察到的元素和它的尺寸信息。

  2. 使用 observe() 方法开始观察元素:

    这将开始观察 element 的尺寸变化。

  3. 使用 unobserve() 方法停止观察单个元素,或使用 disconnect() 方法停止观察所有元素:

本题详细解读

Resize Observer API 的作用

ResizeObserver 提供了一种高效且规范的方式来监听元素的尺寸变化。它解决了传统方式(如 window.onresize 和轮询)的不足:

  • 效率高: ResizeObserver 只在元素实际尺寸发生变化时才触发回调,避免了不必要的计算和性能开销。
  • 精确: 能够准确地获取元素的内容盒子、内边距盒子和边框盒子的大小,提供了更精细的控制。
  • 针对元素而非窗口: window.onresize 监听的是整个窗口的尺寸变化,而 ResizeObserver 可以针对单个元素进行监听,更灵活。
  • 异步: 回调函数会在浏览器布局和绘制完成后异步执行,避免了在布局计算期间修改 DOM 而导致的布局抖动问题。

ResizeObserver 的用法详解

1. 创建 ResizeObserver 实例

使用 new ResizeObserver(callback) 来创建一个 ResizeObserver 实例,其中 callback 是一个回调函数,会在元素尺寸发生变化时被触发。回调函数会接收一个 entries 数组作为参数,该数组包含一个或多个 ResizeObserverEntry 对象,每个对象都包含了一个被观察的元素及其最新的尺寸信息。

2. 观察元素

使用 observe(element) 方法来开始观察特定元素的尺寸变化。

可以在同一ResizeObserver实例上多次调用observe以监听多个元素。

3. 取消观察

使用 unobserve(element) 方法来停止观察某个特定的元素,或使用 disconnect() 方法来停止观察所有元素。

4. ResizeObserverEntry 对象

回调函数中接收的 entries 数组中的每个对象都是一个 ResizeObserverEntry 实例,该实例提供了以下属性:

  • target: 被观察的元素。
  • contentRect: 一个 DOMRectReadOnly 对象,包含了元素内容盒子的尺寸和位置信息。
  • borderBoxSize:一个只读数组,包含了元素的边框盒子大小,数组中每个元素都是一个 ResizeObserverSize 对象,包括 inlineSize 和 blockSize。仅部分浏览器支持。
  • devicePixelContentBoxSize:一个只读数组,包含了元素的内容盒子大小,以设备像素表示,数组中每个元素都是一个 ResizeObserverSize 对象,包括 inlineSize 和 blockSize。仅部分浏览器支持。

5. 实际应用

ResizeObserver 常用于以下场景:

  • 自适应布局: 根据元素尺寸变化动态调整布局。
  • 图表和可视化: 根据容器尺寸变化动态调整图表的绘制。
  • 响应式图片: 根据容器尺寸变化动态加载不同大小的图片。
  • 自定义组件: 创建可根据自身尺寸动态调整的自定义组件。

6. 注意事项

  • ResizeObserver 是异步的,回调函数会在浏览器布局完成后执行。
  • 如果在回调函数中修改了 DOM,可能会触发新的布局计算,从而可能导致新的回调执行,需要小心处理,避免无限循环。
  • 并非所有浏览器都完全支持 ResizeObserver 的所有特性(如borderBoxSize和devicePixelContentBoxSize),需要考虑兼容性。可以通过 polyfill 来支持老旧浏览器。