Motivation
响应式网站 / web 应用程序 根据视口大小调整内容展示方式. 这通常通过 CSS 和 media 查询来完成. 当 CSS 表现不好我们会使用 Javascript.
比如 document.addEventListener("resize",fun) 或者 Element 的 onresize 属性. 通过监听 window.resize 事件, Javascript DOM 操作与视口大小保持同步.
但你会意识到, 这甚至不包括在窗口未被调整大小但元素改变其大小的情况. 例如, 添加新的子元素, 设置元素的 display 样式 none 或类似的操作会改变元素, 其兄弟或祖先的大小.
随着响应式 Web 应用程序的普及, 对响应式组件的需求也会增加. 这些组件也需要有对 resize 事件做出响应. 不幸的是, Web 平台目前不提供组件跟踪其大小的方法.
Current workarounds
一些应用程序实施自制的调整大小通知框架 (例如: Polymer). 这种方法容易出错, 难以维护, 并且需要每个组件都实施自制方法.
其他人巧妙的通过可以代替调整事件的事件来调整内容 (例如:<a href="https://github.com/wnr/element-resize-detector">element-resize-detector</a>). 目前最优秀的方法都使用类似的技巧:
在组件中插入一个绝对定位的子项, 并且以发出滚动事件的方式制作子项, 或者在父项大小更改时制作 window.resize.
绝对定位的子项方法在 ShadowDOM 或 React 等框架中不起作用.
这些方法都不可取. 它们在正确性, 代码复杂性和性能方面都失败了.
在当今的 Web 平台上无法复制 ResizeObserver 功能.
这就是为什么 ResizeObserver 是一个有用的原始 API. 它对任何观察到的元素的大小的变化作出反应, 与导致变化的原因无关. 它还为您提供访问观察元素的新大小.
API
提到的 "Observer" 后缀的 API 共享一个简单的 API 设计. ResizeObserver 也不例外.
您创建一个 ResizeObserver 对象并将回调传递给构造函数. 回调将被赋予一个数组 ResizeOberverEntries- 每个观察元素一个条目 - 包含元素的大小
- 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);
- Usage examples
以下是使用 ResizeObserver 在画布内绘制椭圆的示例.
- <canvas style="width:10%;height:10%"></canvas>
- <canvas style="width:20%;height:20%"></canvas>
- function drawEllipse(entry) {
- let ctx = entry.target.getContext('2d');
- let rx = Math.floor(entry.contentRect.width / 2);
- let ry = Math.floor(entry.contentRect.height / 2);
- ctx.beginPath();
- ctx.clearRect(0,0, entry.contentRect.width,entry.contentRect.height);
ctx.ellipse(rx, ry, rx, ry, 0, 0, 2 * Math.PI);
- ctx.stroke();
- }
- // ResizeObserver delegates action to Element's handleResize method
- let ro = new ResizeObserver( entries => {
- for (let entry of entries) {
- if (entry.target.handleResize)
- entry.target.handleResize(entry);
- }
- });
- // Set up observations
- var canvases = document.querySelectorAll('canvas');
- for (let canvas of canvases) {
- canvas.handleResize = drawEllipse;
- ro.observe(canvas);
- }
内联框架可以检测其大小何时发生变化, 并通知父窗口.
- let ro = new ResizeObserver(entries => {
- let idealSize = computeIdealSize();
- window.parent.postMessage({
- name: "iframeResize",
- width: idealSize.width,
- height: idealSize.height
- }, '*');
- });
- ro.observe(document.body);
当新消息到达时, 我们如何让聊天窗口滚动到底部? ResizeObserver 解决方案将所有消息保存在不断增长的中 div, 并观察其大小. 当新消息到达时, 滚动到底部.
完整的例子 详细讨论了用户滚动的处理.
- .chat {
- overflow: scroll;
- }
- <div class="chat"> <!-- chat has the scrollbar -->
- <div class="chat-text"> <!-- chat-text contains chat text -->
- <div>jack: hi </div>
- <div>jill: hi </div>
- </div>
- </div
- let ro = new ResizeObserver( entries => {
- for (let e of entries) {
- let chat = e.target.parentNode;
- chat.scrollTop = chat.scrollHeight - chat.clientHeight;
- }
- });
- ro.observe(document.querySelector('.chat-text'))
- How
Performance
调整通知的大小可以有很高的频率. Observer API 避免了事件捕获 / 泡泡的开销.
框架作者可以在 ResizeObserver 之上提供一个开发友好的 "基于事件" 的 API, 以避免注册太多的观察者.
Notice
通知传送顺序
当多个 ResizeObservers 注册时, 通知应按注册顺序传送.
回调变更集应按注册顺序列出元素.
https://github.com/WICG/ResizeObserver/blob/master/explainer.md#inline-elements 内联元素
内联元素不应该生成调整大小通知.
https://github.com/WICG/ResizeObserver/blob/master/explainer.md#what-about-transforms 怎么样变换?
转换不会影响内容大小. 他们不应该触发通知.
https://github.com/WICG/ResizeObserver/blob/master/explainer.md#what-about-animations 动画怎么样?
影响内容大小的动画应该会触发通知.
如果工作成本很高, 开发人员可能会希望在动画期间跳过工作.
https://github.com/WICG/ResizeObserver/blob/master/explainer.md#resizing-and-visibility 调整大小和可视性
当元素不可见时, 内容大小变为 0. 这将生成一个调整大小的通知. 开发人员将能够使用 ResizeObserver 观察可见性.
本文参考:
- ResizeObserver: It's Like document.onresize for Elements ----- https://developers.google.com/web/updates/2016/10/resizeobserver
- WICG/ResizeObserver ----- https://github.com/WICG/ResizeObserver/blob/master/explainer.md
来源: https://www.cnblogs.com/CrossGod/p/ResizeObserver.html