Polyfills the ResizeObserver API.
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 3 年前 | ||
| 5 年前 | ||
| 5 年前 | ||
| 3 年前 | ||
| 3 年前 | ||
| 7 年前 | ||
| 7 年前 | ||
| 7 年前 | ||
| 7 年前 | ||
| 3 年前 | ||
| 6 年前 | ||
| 3 年前 | ||
| 3 年前 | ||
| 6 年前 | ||
| 6 年前 |
Resize Observer
这是一个小型库,用于实现ResizeObserver API,并完全基于最新的规范。当元素尺寸变化时,它能立即检测并返回准确的尺寸信息。更多关于使用和性能的信息,请查看示例沙盒。
最新的Resize Observer 规范尚未最终确定,可能会有所变更。 如果规范发生重大改变,此库的主要版本号也会相应升级,因为可能存在破坏性更改。更多信息请查看发行日志。
安装
npm install @juggle/resize-observer
基本用法
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('Body已调整大小!');
observer.disconnect(); // 停止观察
});
ro.observe(document.body); // 监听body的尺寸变化
即使浏览器原生支持ResizeObserver,这也将使用ponyfilled版本。
监视多个元素
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('有', entries.length, '个元素发生了尺寸变化');
entries.forEach((entry, index) => {
const { inlineSize: width, blockSize: height } = entry.contentBoxSize[0];
console.log(`元素${index + 1}:`, `${width}x${height}`);
});
});
const els = document.querySelectorAll('.resizes');
Array.from(els).forEach(el => ro.observe(el)); // 监听多个元素!
监视不同的盒子尺寸
最新标准允许监视不同类型的盒子尺寸。可以在观察元素时指定盒子尺寸选项。可选值包括:border-box,device-pixel-content-box 和 content-box(默认)。
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('有', entries.length, '个元素发生了尺寸变化');
entries.forEach((entry, index) => {
const [size] = entry.borderBoxSize;
console.log(`元素${index + 1}:`, `${size.inlineSize}x${size.blockSize}`);
});
});
// 监视border-box
const observerOptions = {
box: 'border-box'
};
const els = document.querySelectorAll('.resizes');
Array.from(els).forEach(el => ro.observe(el, observerOptions));
自规范:
盒子尺寸属性以序列形式暴露,是为了支持在multi-column场景中出现的具有多个片段的元素。然而,当前对content矩形和边框矩形的定义并没有说明它们如何受到多列布局的影响。在这个规范中,序列中只会返回一个ResizeObserverSize,对应于第一列的尺寸。这个规范的未来版本将扩展返回的序列,包含每个碎片的尺寸信息。
使用早期版本的contentRect
API的早期版本返回contentRect。为了向后兼容,仍然可以使用这个特性。
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
console.log('有', entries.length, '个元素发生了尺寸变化');
entries.forEach((entry, index) => {
const { width, height } = entry.contentRect;
console.log(`元素${index + 1}:`, `${width}x${height}`);
});
});
const els = document.querySelectorAll('.resizes');
Array.from(els).forEach(el => ro.observe(el));
在原生和polyfill版本之间切换
你可以检查浏览器是否支持原生版本,并根据支持情况在原生和polyfill版本之间切换,以提高在支持原生版本的浏览器上的性能。
import { ResizeObserver as Polyfill } from '@juggle/resize-observer';
const ResizeObserver = window.ResizeObserver || Polyfill;
// 根据浏览器支持选择使用原生还是polyfill。
const ro = new ResizeObserver((entries, observer) => {
console.log('有东西调整了大小!');
});
为了进一步优化,你还可以使用动态导入,只在需要polyfill时加载文件。
(async () => {
if ('ResizeObserver' in window === false) {
// 只在需要时异步加载polyfill。
const module = await import('@juggle/resize-observer');
window.ResizeObserver = module.ResizeObserver;
}
// 根据浏览器支持选择使用原生还是polyfill。
const ro = new ResizeObserver((entries, observer) => {
console.log('有东西调整了大小!');
});
})();
注意:某些浏览器的原生支持可能落后于最新规范。 当在原生和polyfill版本间切换时,建议使用
entry.contentRect。
检测无限循环重排
Resize Observers 内置防止无限重排循环的保护机制。
如果在一个重排循环内,被观察元素的盒子尺寸再次发生变化,那么这次观察会被跳过,并在窗口上调度一个错误事件。未送达的通知将在下一个循环中考虑交付。
import { ResizeObserver } from '@juggle/resize-observer';
const ro = new ResizeObserver((entries, observer) => {
// 在观察器内部改变body大小会导致重排循环
// 下一次观察将会被跳过
document.body.style.width = '50%';
});
// 监听错误
window.addEventListener('error', e => console.log(e.message));
// 观察body
ro.observe(document.body);
通知调度
通知在所有其他更改完成并且所有其他动画回调已被调用之后调度。这样,观察器回调就能获取到元素最精确的尺寸,因为在同一帧中不应发生其他更改。

如何检测差异?
为了避免每帧都频繁检查,只有在可能发生元素尺寸变化的事件发生时,才会查询DOM。例如,当点击元素、添加DOM节点或运行动画时。
为了覆盖这些场景,有两种观察方式。首先,监听特定的DOM事件,如resize、mousedown和focus等。其次,监听DOM的任何变动,这会检测到DOM节点的增删、属性修改,甚至文本的变化。
这样可以在应用本身空闲时,获得更多的无操作时间。
功能特性
- 内置的尺寸调整循环保护机制。
- 支持伪类
:hover、:active和:focus。 - 支持过渡效果和动画,包括无限循环和长时间运行的动画。
- 能够检测动画帧期间发生的变更。
- 遵循最新草稿规范,观察不同的盒子尺寸变化。
- 按需轮询,之后自动关闭,有效降低CPU使用率。
- 零延迟系统 – 将通知批量处理并立即送达,早于下一帧渲染。
局限性
- 无法检测具有初始延迟的过渡效果。*
- 若动画和过渡在长时间内无变化,则不会被监测到。*
- 开发者工具中非行内样式的改变只有在转为行内样式时才会被识别。*
测试过的浏览器
- [Chrome][chrome]
- [Safari][safari]
- [Safari iOS][safari-ios]
- [Firefox][ff]
- [Opera][opera]
- [Opera Mini][opera-mini]
- [Edge 12-18][edge_12-18]
- [Edge][edge]
- [Samsung Internet][samsung]
- [Internet Explorer][ie]
台式机平台
| ![Chrome][chrome] | ![Safari][safari] | ![Firefox][ff] | ![Opera][opera] | ![Edge][edge] | ![Edge 12-18][edge_12-18] | ![Internet Explorer][ie] |
|---|---|---|---|---|---|---|
| Chrome | Safari | Firefox | Opera | Edge | Edge 12-18 | IE11 IE 9-10(需垫片)** |
移动平台
| ![Chrome][chrome] | ![Safari][safari] | ![Firefox][ff] | ![Opera][opera] | ![Opera Mini][opera-mini] | ![Edge][edge_12-18] | ![Samsung Internet][samsung] |
|---|---|---|---|---|---|---|
| Chrome | Safari | Firefox | Opera | Opera Mini | Edge | Samsung Internet |
*当有其他交互发生时,变动会被捕捉到。
**IE10需要额外的垫片来支持WeakMap、MutationObserver及devicePixelRatio;IE9除上述外还需垫片支持requestAnimationFrame。更多信息,请参考此处问题。