resize-observer:基于 JavaScript 的 ResizeObserver API 兼容库项目

Polyfills the ResizeObserver API.

分支6Tags65
文件最后提交记录最后更新时间
3 年前
5 年前
5 年前
3 年前
3 年前
7 年前
7 年前
7 年前
7 年前
3 年前
6 年前
3 年前
3 年前
6 年前
6 年前
Resize Observer Logo

Resize Observer

CircleCI Build Status Coveralls Code Coverage Minified + Gzipped Size License

这是一个小型库,用于实现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-boxdevice-pixel-content-boxcontent-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事件,如resizemousedownfocus等。其次,监听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需要额外的垫片来支持WeakMapMutationObserverdevicePixelRatio;IE9除上述外还需垫片支持requestAnimationFrame。更多信息,请参考此处问题