ResizeObserver:类似于元素的 document.onresize
在 ResizeObserver
之前,您必须将监听器附加到文档的 resize
事件,以在视口尺寸更改时获取通知。在活动中 然后,您必须找出哪些元素受到了 并调用特定日常安排以做出适当反应。如果您需要 调整大小后元素的新尺寸,您必须调用 getBoundingClientRect()
或 getComputedStyle()
,这可能会导致布局 如果您不负责批处理所有读取操作和所有操作,就会出现抖动。 写入。
这甚至没有涵盖在没有主要元素的情况下元素会改变其大小的情况 窗口大小例如,附加新的子节点、设置一个 元素的 display
样式更改为 none
,或者类似的操作可以更改 元素、其同级元素或祖先实体。
正因如此,ResizeObserver
才是有用的基元。它会根据 任何观察到的元素的大小,无论发生变化的原因是什么。 它还提供对所观察元素的新大小的访问权限。
API
所有带我们前面提到的 Observer
后缀的 API 共用一个简单的 API 设计。ResizeObserver
也不例外。您创建一个 ResizeObserver
对象 并向构造函数传递一个回调。系统会向该回调函数传递一个 ResizeObserverEntry
对象(每个观察到的元素一个条目), 包含元素的新尺寸。
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
一些详细信息
上报哪些内容?
一般来说, ResizeObserverEntry
通过名为 contentRect
,该函数会返回 DOMRectReadOnly
对象。内容框是用来放置内容的框。时间是 边框减去内边距
请务必注意,尽管ResizeObserver
报告 contentRect
和内边距后,它只会观察 contentRect
。 请勿将 contentRect
与元素的边界框混淆。边界 根据 getBoundingClientRect()
的报告,box 是一个包含 整个元素及其后代。SVG 不遵守此规则 ResizeObserver
会报告边界框的尺寸。
自 Chrome 84 起,ResizeObserverEntry
有了三项新属性,可提供更多 。这两个属性都会返回一个 ResizeObserverSize
对象包含 blockSize
属性和 inlineSize
属性。这个 与在调用回调函数时观察到的元素相关的信息。
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
所有这些项都会返回只读数组,因为我们希望将来能够 它们可以支持具有多个 fragment 的元素,这些 fragment 出现在 多列情景。目前,这些数组将只包含一个元素。
针对这些属性的平台支持有限,但 Firefox 已经 支持 前两项。
何时上报?
该规范规定,ResizeObserver
应处理所有调整大小事件 绘制之前和布局之后这会使 ResizeObserver
的回调成为 更改网页布局的理想位置。因为ResizeObserver
处理发生在布局和绘制之间,这样做只会使 而不是绘制
好问题
您可能会问自己:如果我更改了观察对象的大小,会出现什么情况? ResizeObserver
回调中的元素吗?答案是:触发 立即再次调用该回调函数。幸运的是,ResizeObserver
具有 机制来避免无限回调循环和循环依赖项。更改将 仅当调整后的元素在 DOM 中更深时,才会在同一帧中处理 与在上一个回调中处理的 shallowest 元素相比。 否则,它们会被推迟到下一帧。
应用
ResizeObserver
允许您做的一件事是实现元素级 。通过观察元素,您可以命令式地定义您的 设计断点和更改元素的样式。在以下 example,第二个框 将会根据其宽度更改边框半径。
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
另一个有趣的例子是聊天窗口。出现的问题 即滚动定位。为避免 如果窗口固定在 会话,其中会显示最新邮件。此外,任何类型的布局 (假设手机从横向转为纵向,或者从横向转为纵向) 实现同样的目标。
ResizeObserver
允许您编写一段单一代码, 两种情况。窗口大小调整是 ResizeObserver
可以执行的操作 根据定义进行捕获,但调用 appendChild()
也会调整该元素的大小 (除非已设置 overflow: hidden
),因为它需要为新的 元素。考虑到这一点,只需很少的代码行,即可实现所需的 效果:
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
挺酷的,对吧?
在这里,我可以添加更多代码来处理用户滚动网页的情况 并希望在用户收到新消息时一直滚动该消息 。
另一个用例是任何具有自己的布局的自定义元素。 在ResizeObserver
之前,您没法通过可靠的方法收到 尺寸更改,以便重新排列其子项。
对下一次绘制的交互的影响 (INP)
Interaction to Next Paint (INP) 指标可衡量 网页对用户互动的整体响应情况。如果网页的 INP 为 “优质”阈值,即 200 可以说网页能够可靠地响应 用户与应用之间的互动行为
虽然事件回调的运行时间是为响应 用户互动会显著增加互动的总延迟时间 这并不是 INP 需要考虑的唯一方面。INP 还会考虑 对互动进行下一次绘制所用的时间。这是 更新用户所需的渲染工作所花费的时间 响应互动操作来完成。
就 ResizeObserver
而言,这很重要,因为 ResizerObserver
实例运行发生在渲染工作之前。这个 是设计使然,因为回调中发生的工作必须纳入 因此这项工作很可能需要更改 界面。
根据 ResizeObserver
中的要求尽量减少渲染工作 因为过度渲染工作可能会导致浏览器出现 在处理重要事务方面有延迟。例如,如果任意互动具有 回调,以确保您执行了ResizeObserver
以提供尽可能顺畅的体验:
- 确保 CSS 选择器尽可能简单,从而避免 过多的样式重新计算工作。 样式重新计算发生在布局之前,并且复杂的 CSS 选择器可能 延迟布局操作。
- 避免在
ResizeObserver
回调中执行任何可能会触发的工作 强制自动重排。 - 通常,更新网页布局所需的时间 页面上 DOM 元素的数量。无论网页是否使用
ResizeObserver
时,在ResizeObserver
回调中完成的工作可以变为 网页结构复杂程度的提升会显著增加。
总结
ResizeObserver
支持所有主要语言 浏览器 并且提供了一种高效的方式来监控元素上元素的大小调整 。但请注意,不要让这个强大的 API 呈现太多延迟。