1. 介绍
本节是非规范性的。
响应式Web组件需要对Element
的大小变化做出响应。一个例子是一个显示地图的Element
:
-
它通过在内容框中铺设
Element
瓦片来显示地图。 -
当调整大小时,必须重新铺设瓦片。
响应式Web应用程序已经可以响应视口大小的变化。
这可以通过CSS媒体查询或window.resize
事件来完成。
ResizeObserver API是一个用于观察Element大小变化的接口。它是Element的对应物,类似于window.resize
事件。
ResizeObserver的通知可用于响应Element
大小的变化。关于这些观察的一些有趣事实:
-
当观察的Element插入或从DOM中移除时,将触发观察。
-
当观察的Element的display被设置为none时,将触发观察。
-
对于非替换的内联元素,观察不会触发。
-
CSS变换不会触发观察。
-
当Element正在渲染且Element的大小不是0,0时,启动观察时将触发观察。
< canvas id = "elipse" style = "display:block" ></ canvas > < div id = "menu" style = "display:block;width:100px" > < img src = "hamburger.jpg" style = "width:24px;height:24px" > < p class = "title" > menu title</ p > </ div >
// 响应调整大小时,elipse在canvas内绘制一个椭圆 document. querySelector( '#elipse' ). handleResize= entry=> { entry. target. width= entry. borderBoxSize[ 0 ]. inlineSize; entry. target. height= entry. borderBoxSize[ 0 ]. blockSize; let rx= Math. floor( entry. target. width/ 2 ); let ry= Math. floor( entry. target. height/ 2 ); let ctx= entry. target. getContext( '2d' ); ctx. beginPath(); ctx. ellipse( rx, ry, rx, ry, 0 , 0 , 2 * Math. PI); ctx. stroke(); } // 响应调整大小时,根据宽度更改标题可见性 document. querySelector( '#menu' ). handleResize= entry=> { let title= entry. target. querySelector( ".title" ) if ( entry. borderBoxSize[ 0 ]. inlineSize< 40 ) title. style. display= "none" ; else title. style. display= "inline-block" ; } var ro= new ResizeObserver( entries=> { for ( let entryof entries) { let cs= window. getComputedStyle( entry. target); console. log( '观察元素:' , entry. target); console. log( entry. contentRect. top, ' 是 ' , cs. paddingTop); console. log( entry. contentRect. left, ' 是 ' , cs. paddingLeft); console. log( entry. borderBoxSize[ 0 ]. inlineSize, ' 是 ' , cs. width); console. log( entry. borderBoxSize[ 0 ]. blockSize, ' 是 ' , cs. height); if ( entry. target. handleResize) entry. target. handleResize( entry); } }); ro. observe( document. querySelector( '#elipse' )); ro. observe( document. querySelector( '#menu' ));
2. 调整观察器 API
2.1. ResizeObserver 接口
ResizeObserver 接口用于观察Element
的大小变化。
它基于MutationObserver
和IntersectionObserver
。
enum {
ResizeObserverBoxOptions ,
"border-box" ,
"content-box" };
"device-pixel-content-box"
ResizeObserver 可以观察不同类型的CSS大小:
-
border-box
:由CSS2定义的边框区域的大小。 -
content-box
:由CSS2定义的内容区域的大小。 -
device-pixel-content-box
:由CSS2定义的内容区域的大小,以设备像素表示,且在应用任何CSS变换之前。此大小必须包含整数值。
device-pixel-content-box
可以通过将devicePixelRatio乘以content-box
的大小来近似计算。
但由于浏览器特定的子像素对齐行为,作者无法确定正确的舍入方式。
UA如何计算元素的设备像素框是依赖于具体实现的。一种可能的实现是将框的大小和位置乘以设备像素比,然后舍入生成的浮点大小和位置,以最大限度地提高渲染输出的质量。
注意,此大小可能会受到目标位置变化的影响,因此通常比其他大小的观察更昂贵。
dictionary {
ResizeObserverOptions ResizeObserverBoxOptions = "content-box"; };
box
本节是非规范性的。作者可能希望观察多个CSS框。在这种情况下,作者需要使用多个ResizeObserver。
// 观察 content-box ro. observe( document. querySelector( '#menu' ), { box: 'content-box' }); // 仅观察边框框。替换之前的观察。 ro1. observe( document. querySelector( '#menu' ), { box: 'border-box' });
这不会影响在事件触发时返回给定义回调的框尺寸,它仅定义作者希望观察布局变化的框。
[Exposed =(Window ),Constructor (ResizeObserverCallback )]
callback interface {
ResizeObserver void observe (Element ,
target optional ResizeObserverOptions );
options void unobserve (Element );
target void disconnect (); };
new ResizeObserver(callback)
-
-
让 this 成为新的
ResizeObserver
对象。 -
将 this 的 callback 内部槽设置为 callback。
-
将 this 的 observationTargets 内部槽设置为空列表。
-
将 this 添加到
Document
的 resizeObservers 槽中。
-
observe(target, options)
-
将目标添加到观察元素列表中。
-
如果 target 已在
observationTargets
槽中,调用 unobserve(target)。 -
创建新的 resizeObservation,使用
ResizeObservation
(target, options)。 -
将 resizeObservation 添加到 observationTargets 槽中。
-
unobserve(target)
-
将 target 从观察元素列表中移除。
-
在
ResizeObservation
中找到目标为 target 的 observation。 -
如果未找到 observation,则返回。
-
从
observationTargets
中移除 observation。
-
disconnect()
-
-
清空
observationTargets
列表。 -
清空
activeTargets
列表。
-
2.2. ResizeObserverCallback
callback =
ResizeObserverCallback void (sequence <ResizeObserverEntry >,
entries ResizeObserver );
observer
此回调传递 ResizeObserver
的通知。它由广播活动观察算法调用。
2.3. ResizeObserverEntry
[Exposed =Window ]interface {
ResizeObserverEntry readonly attribute Element target ;readonly attribute DOMRectReadOnly contentRect ;readonly attribute sequence <ResizeObserverSize >borderBoxSize ;readonly attribute sequence <ResizeObserverSize >contentBoxSize ;readonly attribute sequence <ResizeObserverSize >devicePixelContentBoxSize ; };
contentRect 来源于 ResizeObserver 的孵化阶段,目前仅出于 Web 兼容性原因而包含。它可能在未来的级别中被弃用。
target
, 类型为 Element,只读-
大小发生变化的
Element
。 contentRect
, 类型为 DOMRectReadOnly,只读-
Element
的 内容矩形,在ResizeObserverCallback
调用时。 borderBoxSize
, 类型为 sequence<ResizeObserverSize>,只读-
包含
Element
的 边框盒 大小的序列,在ResizeObserverCallback
调用时。 contentBoxSize
, 类型为 sequence<ResizeObserverSize>,只读-
包含
Element
的 内容矩形 大小的序列,在ResizeObserverCallback
调用时。 devicePixelContentBoxSize
, 类型为 sequence<ResizeObserverSize>,只读-
包含
Element
的内容矩形大小的序列,以设备像素为单位,在ResizeObserverCallback
调用时。
盒大小属性以序列的形式暴露,以支持具有多个片段的元素, 这种情况出现在 多列 场景中。 但当前 内容矩形 和 边框盒 的定义并未提及这些盒在 多列 布局中的影响。 在本规范中,序列中只会返回一个 ResizeObserverSize, 该尺寸将对应于第一列的尺寸。 未来版本的此规范将扩展返回的序列,以包含每个片段的大小信息。
interface {
ResizeObserverSize readonly attribute unrestricted double ;
inlineSize readonly attribute unrestricted double ; };
blockSize
3. 处理模型
3.1. ResizeObservation 示例结构
本节为非规范性内容。ResizeObservation 是一个示例结构体,可以用于实现 Resize
Observer。它被包含在此处以帮助在处理模型期间提供清晰的说明。它有效地保存了单个 Element
的观察信息。此接口对 Javascript 不可见。
[(
Constructor Element ) ]
target interface {
ResizeObservation readonly attribute Element target ;readonly attribute ResizeObserverBoxOptions observedBox ;readonly attribute sequence <ResizeObserverSize >lastReportedSizes ; };
target
, 类型为 Element,只读-
被观察的
Element
。 observedBox
, 类型为 ResizeObserverBoxOptions,只读-
正在被观察的盒模型。
lastReportedSizes
, 类型为 sequence<ResizeObserverSize>,只读-
按顺序记录的上次报告的大小。
new ResizeObservation(target, observedBox)
-
-
令 this 为一个新的
ResizeObservation
对象 -
将 this 内部的
target
槽设置为 target -
将 this 内部的
observedBox
槽设置为 observedBox -
将 this 内部的
lastReportedSizes
槽设置为 [(0,0)]
-
isActive()
-
-
通过 计算盒子大小 并根据 target 和 observedBox 设置 currentSize。
-
如果 currentSize 不等于
lastReportedSizes
中的第一个条目,则返回 true。 -
返回 false。
-
3.2. 内部槽定义
3.2.1. 文档
文档 有一个 resizeObservers
槽,它是该文档中 ResizeObserver
的列表。它初始化为空。
3.2.2. ResizeObserver
ResizeObserver
有一个 callback
槽,由构造函数初始化。
ResizeObserver
有一个 observationTargets
槽,它是 ResizeObservation
的列表。
它代表所有被观察的元素。
ResizeObserver
有一个 activeTargets
槽,它是 ResizeObservation
的列表。它代表自上次观察广播以来尺寸已更改且符合广播条件的所有元素。
ResizeObserver
有一个 skippedTargets
槽,它是 ResizeObservation
的列表。它代表自上次观察广播以来尺寸已更改但不符合广播条件的所有元素。
3.3. CSS 定义
3.3.1. 内容矩形
DOM 内容矩形 是一个矩形,其:内容宽度 规范未提及 多列 布局如何影响内容盒。在本规范中,多列 内 元素
的内容宽度是 getComputedStyle(element).width
的结果。目前,这会计算出第一列的宽度。
将内容矩形的位置设置为内边距顶部/左侧对于目标子元素的绝对定位很有用。绝对定位坐标空间的原点是内边距矩形的左上角。
观察内容矩形意味着:
-
当观察的元素被插入/移除 DOM 时,观察将触发。
-
当观察的元素显示设置为 none 时,观察将触发。
-
非替换行内元素将始终有一个空的内容矩形。
-
观察不会因 CSS 变换而触发。
Web 内容也可以包含 SVG 元素。SVG 元素定义 边界框 而不是内容盒。 SVGGraphicsElement 的内容矩形是一个矩形,其:
3.4. 算法
3.4.1. 按深度收集活动观察
它计算 document 的所有活动观察。要 按深度收集活动观察,请执行以下步骤:
-
将 depth 设为传入的深度。
-
对于每个 observer 在
resizeObservers
中运行这些步骤:-
清除 observer 的
activeTargets
和skippedTargets
。 -
对于每个 observation 在 observer.
observationTargets
运行此步骤:-
如果 observation.
isActive()
为 true-
如果 targetDepth 大于 depth,则将 observation 添加到
activeTargets
。 -
否则将 observation 添加到
skippedTargets
。
-
-
3.4.2. 是否有活动观察
要确定 Document
是否有活动观察,请运行以下步骤:
-
对于每个 observer 在
resizeObservers
中运行此步骤:-
如果 observer.
activeTargets
不为空,则返回 true。
-
-
返回 false。
3.4.3. 是否有跳过的观察
要确定 Document
是否有跳过的观察,请运行以下步骤:
-
对于每个 observer 在
resizeObservers
中运行此步骤:-
如果 observer.
skippedTargets
不为空,则返回 true。
-
-
返回 false。
3.4.4. 创建并填充 ResizeObserverEntry
要 创建并填充 ResizeObserverEntry,针对给定的 target,请运行以下步骤:-
将 this 设为一个新的
ResizeObserverEntry
。 -
将 this.
target
槽设置为 target。 -
将 this.
borderBoxSize
槽设置为基于 计算大小,给定 target 和 "border-box" 的 observedBox 的结果。 -
将 this.
contentBoxSize
槽设置为基于 计算大小,给定 target 和 "content-box" 的 observedBox 的结果。 -
将 this.
devicePixelContentBoxSize
槽设置为基于 计算大小,给定 target 和 "device-pixel-content-box" 的 observedBox 的结果。 -
将 this.
contentRect
设为逻辑上的 this.contentBoxSize
,给定 target 和 "content-box" 的 observedBox。 -
如果 target 不是 SVG 元素,执行以下步骤:
-
如果 target 是 SVG 元素,执行以下步骤:
-
将 this.contentRect.top 和 this.contentRect.left 设为 0。
-
3.4.5. 广播活动观察
广播活动观察 在文档中传递所有活动观察,并返回最浅广播目标深度。
要为 document 广播活动观察,请运行以下步骤:
-
将 shallowestTargetDepth 设为 ∞
-
对于每个 observer 在 document.
resizeObservers
中运行这些步骤:-
如果 observer.
activeTargets
槽为空,则继续。 -
将 entries 设为空的
ResizeObserverEntry
列表。 -
对于每个 observation 在
activeTargets
中,执行以下步骤:-
将 entry 设为运行 创建并填充 ResizeObserverEntry 的结果,针对 observation.
target
。 -
将 entry 添加到 entries。
-
将 observation.
lastReportedSizes
设为匹配 entry 的大小。-
匹配的大小是 entry.
borderBoxSize
,如果 observation.observedBox
为 "border-box" -
匹配的大小是 entry.
contentBoxSize
,如果 observation.observedBox
为 "content-box" -
匹配的大小是 entry.
devicePixelContentBoxSize
,如果 observation.observedBox
为 "device-pixel-content-box"
-
-
如果 targetDepth < shallowestTargetDepth,则将 shallowestTargetDepth 设为 targetDepth
-
-
调用 observer.
callback
,传递 entries。 -
清除 observer.
activeTargets
。
-
-
返回 shallowestTargetDepth。
3.4.6. 传递 Resize 循环错误
要 传递 Resize 循环错误通知,请运行以下步骤:
-
创建一个新的
ErrorEvent
。 -
将 event 的消息槽初始化为 "ResizeObserver 循环完成但未传递通知"。
-
报告异常 event。
3.4.7. 计算节点深度
要 计算节点深度,给定 node,请运行以下步骤:
-
将 p 设为从 node 到此元素的扁平 DOM 树的根元素的父级遍历路径。
-
返回 p 中的节点数量。
3.4.8. 计算盒子大小,给定目标和观察盒
该算法计算 target Element
的观察盒大小。盒子的类型由 ResizeObserverBoxOptions
描述。
SVG 元素是一个例外。SVG 的大小始终是其边界框大小,因为 SVG 元素不使用标准 CSS 盒模型。
要 计算盒子大小,给定 target 和 observedBox,请运行以下步骤:
-
如果 target 是
SVGGraphicsElement
-
如果 target 不是
SVGGraphicsElement
-
如果 observedBox 是 "border-box"
-
如果 observedBox 是 "content-box"
-
如果 observedBox 是 "device-pixel-content-box"
-
返回 computedSize。
-
3.5. ResizeObserver 生命周期
ResizeObserver
将保持存活,直到满足以下两个条件:
-
没有对观察者的脚本引用。
-
观察者没有观察任何目标。
3.6. 外部规范集成
3.6.1. HTML 处理模型:事件循环
ResizeObserver
的处理发生在 HTML 处理模型 事件循环的步骤 7.12 中。
第 12 步目前的说明为:
对于 docs 中的每个完全活动的文档,更新该文档及其浏览上下文的渲染或用户界面,以反映当前状态。
现有的第 12 步可以详细说明为:
对于 docs 中的每个完全活动的文档,运行以下步骤以用于该文档及其浏览内容:
-
重新计算样式
-
更新布局
-
绘制
ResizeObserver
通过循环添加调整大小通知来扩展第 12 步,直到没有待处理的通知为止。这可能导致无限循环。
通过在每次迭代时缩小可以通知的节点集来防止无限循环。在每次迭代中,只有比上一次迭代中最浅节点更深的节点可以发出通知。
如果通知循环完成并且有未传递的通知,则会生成错误。具有未传递通知的元素将在下一个循环中被考虑进行传递。
具有 ResizeObserver
通知的第 12 步为:
对于 docs 中的每个完全活动的文档,运行以下步骤以用于该文档及其浏览上下文: