ResizeObserver
ResizeObserver
接口报告 Element
的内容或边框框,或 SVGElement
的边界框尺寸的变化。
注意:内容框是可以放置内容的框,即边框框减去填充和边框宽度。边框框包含内容、填充和边框。有关更详细的解释,请参阅 盒子模型。
构造函数
ResizeObserver()
-
创建并返回一个新的
ResizeObserver
对象。
实例属性
无。
实例方法
ResizeObserver.disconnect()
-
取消观察特定观察者的所有观察到的
Element
目标。 ResizeObserver.observe()
-
启动对指定
Element
的观察。 ResizeObserver.unobserve()
-
结束对指定
Element
的观察。
示例
在 resize-observer-text.html (查看源代码) 示例中,我们使用调整大小观察器来更改标题和段落的 font-size
,因为滑块的值发生变化会导致包含 <div>
的宽度发生变化。这表明您可以响应元素大小的变化,即使这些变化与视口无关。
我们还提供了一个复选框来打开和关闭观察器。如果将其关闭,则文本不会响应 <div>
宽度变化而发生变化。
JavaScript 代码如下所示
const h1Elem = document.querySelector("h1");
const pElem = document.querySelector("p");
const divElem = document.querySelector("body > div");
const slider = document.querySelector('input[type="range"]');
const checkbox = document.querySelector('input[type="checkbox"]');
divElem.style.width = "600px";
slider.addEventListener("input", () => {
divElem.style.width = `${slider.value}px`;
});
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = entry.contentBoxSize[0];
h1Elem.style.fontSize = `${Math.max(
1.5,
contentBoxSize.inlineSize / 200,
)}rem`;
pElem.style.fontSize = `${Math.max(
1,
contentBoxSize.inlineSize / 600,
)}rem`;
} else {
h1Elem.style.fontSize = `${Math.max(
1.5,
entry.contentRect.width / 200,
)}rem`;
pElem.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
}
}
console.log("Size changed");
});
resizeObserver.observe(divElem);
checkbox.addEventListener("change", () => {
if (checkbox.checked) {
resizeObserver.observe(divElem);
} else {
resizeObserver.unobserve(divElem);
}
});
观察错误
遵循规范的实现会在绘制之前(即在将帧呈现给用户之前)调用调整大小事件。如果存在任何调整大小事件,则会重新评估样式和布局,这反过来可能会触发更多调整大小事件。循环依赖关系引起的无限循环通过仅在每次迭代期间处理 DOM 中更深层的元素来解决。不满足该条件的调整大小事件会被延迟到下一个绘制,并且会在 Window
对象上触发错误事件,并显示定义明确的消息字符串
ResizeObserver 循环已完成,但存在未传递的通知。
请注意,这只能防止用户代理死锁,而不能防止无限循环本身。例如,以下代码将导致 divElem
的宽度无限增长,并且控制台中会重复显示上述错误消息,每帧都会出现。
const divElem = document.querySelector("body > div");
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
entry.target.style.width = entry.contentBoxSize[0].inlineSize + 10 + "px";
}
});
resizeObserver.observe(divElem);
window.addEventListener("error", (e) => {
console.error(e.message);
});
只要错误事件不会无限期地触发,调整大小观察器就会稳定下来并生成稳定且可能正确的布局。但是,访问者可能会看到布局中断的闪烁,因为原本预期在一帧内发生的更改序列现在却跨越了多帧。
如果要防止这些错误,解决方案取决于您预期的效果。如果您确实打算使用无限循环,则只需要将 ResizeObserver
回调中的调整大小代码延迟到浏览器重绘之后。您可以将其放入 requestAnimationFrame
回调中。
const divElem = document.querySelector("body > div");
const resizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
for (const entry of entries) {
entry.target.style.width = entry.contentBoxSize[0].inlineSize + 10 + "px";
}
});
});
resizeObserver.observe(divElem);
window.addEventListener("error", (e) => {
console.error(e.message);
});
如果您不打算使用无限循环,则应确保您的调整大小代码不会触发调整大小观察器回调。有很多方法可以做到这一点,例如设置“预期大小”并在大小已达到该值时不调整大小。
const divElem = document.querySelector("body > div");
const expectedSizes = new WeakMap();
const resizeObserver = new ResizeObserver((entries) => {
requestAnimationFrame(() => {
for (const entry of entries) {
const expectedSize = expectedSizes.get(entry.target);
if (entry.contentBoxSize[0].inlineSize === expectedSize) {
continue;
}
const newSize = entry.contentBoxSize[0].inlineSize + 10;
entry.target.style.width = `${newSize}px`;
expectedSizes.set(entry.target, newSize);
}
});
});
resizeObserver.observe(divElem);
window.addEventListener("error", (e) => {
console.error(e.message);
});
规范
规范 |
---|
Resize Observer # resize-observer-interface |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。
另请参阅
- 盒子模型
PerformanceObserver
IntersectionObserver
(Intersection Observer API 的一部分)- 即将推出的 容器查询 可能是实现响应式设计的可行替代方案。