1. 简介
高效的网站渲染依赖于用户代理能够检测页面中哪些部分正在显示、哪些部分可能影响当前显示的区域,以及哪些部分可以被忽略。
虽然可以使用各种启发式方法来猜测给定的子树在某种程度上是否独立于页面其余部分,但这些方法很脆弱。页面上的微小改动可能会意外导致其无法通过这些启发式测试,从而使渲染进入缓慢的代码路径。此外,许多需要隔离的内容通过启发式方法很难甚至无法检测到。
为了缓解这些问题并允许对子树与页面其余部分进行强有力、可预测的隔离,本规范定义了 contain 属性。
为了进一步优化屏幕外内容,本规范还定义了 content-visibility 属性,使用户代理能够在不需要时完全跳过元素的布局和渲染。
1.1. 模块交互
本文档定义了早期规范中不存在的新功能。此外,它旨在在稳定后替换并取代 [CSS-CONTAIN-1]。
1.2. 值定义
本规范遵循 CSS 属性定义约定([CSS2]),使用 值定义语法([CSS-VALUES-3])。本规范未定义的值类型在 CSS Values & Units [CSS-VALUES-3] 中定义。与其他 CSS 模块的组合可能会扩展这些值类型的定义。
除了定义中列出的属性特定值外,本规范中定义的所有属性也都接受 CSS 全局关键字 作为其属性值。为了可读性,未明确重复列出。
2. 强包含:contain 属性
| 名称 | contain |
|---|---|
| 值 | none | strict | content | [ size || layout || style || paint ] |
| 初始值 | none(无) |
| 应用于 | 参见下方 |
| 可继承 | 否 |
| 百分比 | 不可用 |
| 计算值 | 关键字 none 或 size, layout, paint 中的一个或多个 |
| 规范顺序 | 按语法 |
| 动画类型 | 不可动画 |
用户代理应在所有介质(包括非可视介质)上支持此属性。
contain 属性允许作者指明一个元素及其内容尽可能地独立于文档树的其余部分。这允许用户代理在正确使用 contain 渲染页面时利用更强大的优化,并使作者确信他们的页面不会因为微小的改动而意外进入缓慢的代码路径。
- none(无)
- 该值表示该属性无效。元素正常渲染,不应用任何包含效果。
- strict
- 该值计算为 size layout paint style,从而为该元素开启所有形式的包含。
- 内容
- 该值计算为 layout paint style,从而为该元素开启除了尺寸包含以外的所有形式的包含。
注: contain: content 应用起来相对“安全”;它的效果在实践中相当有限,大多数内容不会违反其限制。但是,因为它不应用尺寸包含,所以元素仍然可以响应其内容的大小,这可能会导致布局失效向树上层蔓延超过预期。尽可能使用 contain: strict,以获得尽可能多的包含效果。
- size
- 该值开启元素的尺寸包含。这确保包含框可以在无需检查其后代的情况下进行布局。
- 布局 (layout)
- 该值开启元素的布局包含。这确保包含框在布局目的上是完全不透明的;外部没有任何东西可以影响其内部布局,反之亦然。
- style
- 该值开启元素的样式包含。这确保对于不仅对元素及其后代有影响的属性,这些影响不会逃离该元素。
- 绘画 (paint)
- 该值开启元素的渲染包含。这确保包含框的后代不会显示在其边界之外,因此如果一个元素在屏幕外或不可见,其后代也保证是不可见的。
此属性通常适用于所有元素(包括 CSS 伪元素 4 § 4.1 生成内容伪元素:::before 和 ::after),尽管某些类型的包含对某些元素没有影响,详见 § 3 包含的类型。此外,在 [SVG2] 的情况下,contain 属性仅适用于具有关联 CSS 布局框的 svg 元素。
例如,假设一个微博社交网络有类似这样的标记
< body >
< aside > ...</ aside >
< section >
< h2 > Messages</ h2 >
< article >
Lol, check out this dog: images.example.com/jsK3jkl
</ article >
< article >
I had a ham sandwich today. #goodtimes
</ article >
< article >
I have political opinions that you need to hear!
</ article >
…
</ section >
</ body >
站点上可能显示了大量消息,但每条消息都是独立的,不会影响站点上的其他任何内容。因此,每一条消息都可以标记为 contain: content 以将其传达给用户代理,以便它可以优化页面并跳过屏幕外消息的大量计算。如果每条消息的大小是预先知道的,则可以使用 contain: strict 来传达进一步的限制。
此外,当 HTML html 或 body 元素上激活了任何包含时,属性从 body 元素到初始包含块、视口或画布背景的传播将被禁用。值得注意的是,这会影响
-
writing-mode, direction, 和 text-orientation(参见 CSS 书写模式 3 § 8 主要书写模式)
-
overflow 及其简写属性(参见 CSS 溢出 3 § 3.5 溢出视口传播)
-
background 及其简写属性(参见 CSS 背景 3 § 2.11.2 画布背景和 HTML <body> 元素)
注: 属性传播到初始包含块、视口或画布背景,如果是在 html 元素本身设置的,则不受影响。
注: 除了 contain 之外,还有几个属性可以为元素开启各种包含。这些不会影响 contain 的值;例如,一个元素可以有 contain: none,但仍然可以通过 content-visibility 开启布局包含。
3. 封装类型
有几种包含方式可应用于元素,以各种方式限制其后代对页面其余部分产生的影响。包含使用户代理能够进行更强大的优化,并帮助作者用功能单元构建页面,因为它限制了特定更改对文档的影响范围。
引入新属性或机制的规范作者需要考虑各种类型的包含是否以及如何影响他们所引入的内容,并在其规范中包含此处未描述的任何影响。
3.1. 尺寸包含
赋予元素尺寸包含使其主体框成为尺寸包含框,并具有以下效果
-
尺寸包含框的固有尺寸的确定方式就好像该元素没有内容一样,遵循与视为空时调整大小相同的逻辑。
注: 这会影响对 min-content 或 max-content 关键字的显式调用,以及任何依赖于这些测量的计算,例如将尺寸包含项目放入其中时调整网格轨道的大小,或者如果调整包含框父级 fit-content 大小。
-
对尺寸包含框及其内容进行布局在概念上分为两个阶段
- 视为空时调整大小
- 使用中的 width 和 height 的确定方式就好像执行框的正常布局一样,只是它被视为没有内容——
甚至不通过伪元素(例如 ::before, ::after, 或 ::marker)包含任何内容。 替换元素必须被视为具有 0 的固有宽度和高度,且没有固有纵横比。
注: 尺寸包含仅抑制固有纵横比,因此会尊重直接影响该首选纵横比的属性(例如 aspect-ratio)。
尺寸包含框的所有 CSS 属性都会像正常执行布局时那样被考虑在内。其他规范可能会做出特定的豁免。
注: 即使元素的尺寸属性指定了固有尺寸,这也不一定使元素尺寸为零:元素本身设置的属性会继续被考虑在内,这可能导致其变大。
- 原地布局
- 然后,包含框的内容(包括任何伪元素)必须正常地布局到现已固定尺寸的包含框中。
img{ width : 100 px ; aspect-ratio : 1 /1 ; contain : size; } < img src = "https://www.example.com/300x100.jpg" >
如果未声明 aspect-ratio 属性,图像将为 100px x 0px,因为其固有纵横比被抑制,且其固有高度被视为 0。
但是,如果满足以下任何条件,则为元素赋予尺寸包含无效
-
如果元素不生成主体框(如 display: contents 或 display: none 的情况)
注: 不包括表格标题在内的内部表格框被排除在外,因为表格布局算法不允许框变得比其流入内容更小。将表格单元格调整为好像它为空,然后在不改变大小的情况下在其内部布局其内容,实际上是一个未定义的操作。手动将 width 或 height 属性设置为 0 无法使其比其内容更小。此关注点不适用于表格标题,表格标题完全可以具有独立于其内容的固定大小。
3.1.1. 可能的尺寸包含优化
本节是非规范性的。
单独使用尺寸包含本身并不能提供太多优化机会。它自身的主要好处在于,想要基于包含框的大小来布局包含框内容的工具(例如实现“容器查询”概念的 JS 库)可以在不用担心“无限循环”的情况下进行布局,即子项的大小响应包含框的大小导致包含框的大小也发生变化,可能触发进一步的子项调整大小的方式改变,从而可能导致包含框大小更多的变化,以至无穷。
但是,当与布局包含搭配使用时,可以启用的优化包括(但不限于)
-
当包含框的后代的样式或内容发生变化时,计算 DOM 树中哪些部分是“脏的”且可能需要重新布局的操作可以在包含框处停止。
3.2. 布局包含
赋予元素布局包含使其主体框成为布局包含框,并具有以下效果
-
如果分片上下文的至少一个分片容器具有布局包含,或者如果一个分片上下文的至少一个分片容器是布局包含框的后代,且同一个分片上下文的至少一个后续分片容器不是同一个具有布局包含的元素的后代,那么第一个既是分片容器本身又是分片容器的祖先的布局包含框必须“捕获”分片流的剩余部分:分片不得超过布局包含边界,并且第一个布局包含边界内的最后一个分片容器被视为其分片上下文中的最后一个分片容器。
如果分片上下文中的后续分片容器仅在分片流中仍有更多内容时才生成,则不会生成它们。如果无论如何都会存在,它们将保持为分片上下文的一部分,但不会从分片流接收任何内容。
注: 在撰写本文时,没有稳定的规范受到这一点的影响。只关心那些能够使分片上下文的某些(但不是全部)分片容器成为布局包含(或布局包含元素的后代)的规范。这既不是 [CSS-PAGE-3] 的情况,也不是 [CSS-MULTICOL-1] 的情况。尽管如此,此要求被包括在内,因为已经考虑了多种使其成为可能的机制(例如:[CSS-REGIONS-1], ::nth-fragment(),一种用于多列布局的假设选择器……),并且如果此类机制不遵守此规则,则无法实现布局包含旨在提供的保证。 [CSS-REGIONS-1] 详细说明了布局包含如何影响区域。
< article > Lorem ipsum…</ article > < div id = a ></ div > < aside > < div id = b ></ div > < div id = c ></ div > </ aside > < aside > < div id = d ></ div > < div id = e ></ div > </ aside > < div id = f ></ div > article{ flow-into : foo;} #a, #b, #c, #d, #e, #f{ flow-from : foo;} aside{ contain : layout} 在这个 [CSS-REGIONS-1] 示例中,内容可以从
#a流向#b,从#b流向#c。然而,由于#c是第一个布局包含框中的最后一个分片容器,它捕获了所有剩余内容,因此没有任何内容流入#d,#e或#f。 -
强制断点允许在布局包含框内,但不会像 CSS 分片 3 § 3.1 框之间的断点:break-before 和 break-after 属性中所述那样传播到父级。
注: 这引入了以前不存在的可能性,即强制断点可能发生在框及其容器之间(参见 CSS 分片 3 § 4.1 可能的断点)。
-
对于 vertical-align 属性,或任何其他需要将布局包含框的基线位置与除其后代之外的事物相关联的属性,包含框被视为没有基线。
但是,如果满足以下任何条件,则为元素赋予布局包含无效
-
如果元素不生成主体框(如 display: contents 或 display: none 的情况)
-
如果其主体框是内部表格框(非 table-cell)
3.2.1. 可能的布局包含优化
本节是非规范性的。
可以由布局包含启用的优化包括(但不限于)
-
在布局页面时,单独的包含框的内容可以并行布局,因为它们保证不会相互影响。
-
在布局页面时,如果包含框在屏幕外或被遮挡,并且屏幕可见部分的布局不依赖于包含框的大小(例如,如果包含框靠近块容器的末尾,而您正在查看块容器的开头),包含框内容的布局可以延迟或以较低优先级执行。
(当与尺寸包含搭配使用时,此优化可以更自由地应用。)
3.3. 样式包含
赋予元素样式包含具有以下效果
-
counter-increment 和 counter-set 属性必须作用域限定为元素的子树并创建一个新的计数器。
-
content 属性的 open-quote, close-quote, no-open-quote 和 no-close-quote 的效果必须作用域限定为元素的子树。
注: 这意味着子树中引号嵌套的深度保持不变,并从其上下文通常暗示的值开始,但是子树内部这些值对引号嵌套深度的更改不会影响子树外部的引号嵌套深度。
注: [CSS-REGIONS-1] 对样式包含如何影响区域有规范性要求。
-
如果限定作用域为元素,它必须表现得就像作用域元素是文档的根一样,以便评估属性的效果:在作用域元素外部使用该属性对作用域元素上或内部使用该属性没有影响,反之亦然。
注: “限定作用域为元素”目前未使用。它定义为一个供未来规范使用的扩展点。
-
如果限定作用域为子树,情况也是如此,除了作用域元素本身被视为树的“外部”,就像文档的其余部分一样,该属性对该元素的效果不受作用域限制的影响。在考虑作用域属性对子树内部元素的效果时,子树底部的元素被视为文档的根。
1 1.2
< div ></ div >
div {
contain : style;
counter-increment : n;
}
div::before, div::after {
content : counters ( n, '.' ) " " ;
}
div::after {
counter-increment : n 2 ;
}
3.3.1. 可能的样式包含优化
本节是非规范性的。
可以由样式包含启用的优化包括(但不限于)
-
每当具有样式包含的元素的后代上的属性发生变化时,计算 DOM 树中哪些部分是“脏的”且可能需要重新计算样式的操作可以在具有样式包含的元素处停止。
3.4. 渲染包含
赋予元素渲染包含使其主体框成为渲染包含框,并具有以下效果
-
元素的内容(包括任何墨水溢出或可滚动溢出)必须剪裁到渲染包含框的溢出剪裁边缘,同时考虑 [[css-backgrounds-3#corner clipping|角落剪裁]]。这不包括创建任何用于访问或指示被剪裁内容存在的机制;也不会通过其他属性(例如 overflow, resize, 或 text-overflow)抑制此类机制的创建。
注: 此剪裁形状尊重 overflow-clip-margin,允许具有渲染包含的元素仍能稍微超出其正常边界。
注: 本段描述的行为等同于在使用值时将 overflow-x: visible 更改为 overflow-x: clip,将 overflow-y: visible 更改为 overflow-y: clip,同时保留 overflow-x 和 overflow-y 的其他值不变。
但是,如果满足以下任何条件,则为元素赋予渲染包含无效
-
如果元素不生成主体框(如 display: contents 或 display: none 的情况)
-
如果其主体框是内部表格框(非 table-cell)
3.4.1. 可能的渲染包含优化
本节是非规范性的。
可以由渲染包含启用的优化包括(但不限于)
-
如果包含框在屏幕外或被遮挡,UA 通常可以跳过对其内容进行渲染的尝试,因为它们也保证在屏幕外/被遮挡。
注: 某些渲染效果(例如 [FILTER-EFFECTS-1] 中的 blur() 滤镜)具有非局部影响。用户代理需要跟踪这些影响,因为它可能需要在具有此类滤镜的元素的后代发生变化时重新渲染元素的部分,即使它们具有渲染包含且本可以跳过也是如此。
-
除非被剪裁的内容可以通过其他机制(例如 overflow, resize, 或 text-overflow 属性)使之可访问,否则 UA 可以为该框保留正好等于该框大小的“画布”空间。(在类似的可滚动情况下,例如 overflow: hidden,可以滚动到当前被剪裁的内容,因此 UA 通常会预见性地过度渲染一些,以便在滚动发生时立即有内容可见,而不是一帧之后才显示。)
-
因为保证它们是堆叠上下文,可滚动的元素可以被渲染到单个 GPU 层中。
4. 完全抑制元素的内容:content-visibility 属性
| 名称 | content-visibility |
|---|---|
| 值 | visible | auto | hidden |
| 初始值 | visible |
| 应用于 | 可以应用布局包含的元素 |
| 可继承 | 否 |
| 百分比 | 不可用 |
| 计算值 | 按指定值 |
| 规范顺序 | 按语法 |
| 动画类型 | 不可动画 |
content-visibility 属性控制元素是否渲染其内容,并强制执行一组强有力的包含,允许用户代理在需要之前潜在地省略大量的布局和渲染工作。它有以下值
- visible
-
无效果。元素的的内容按正常方式布局和渲染。
- 该组件可以在本包或其他任何使用包的其他组件中被引用;它不能在使用包中被不同的组件覆盖。
-
元素跳过其内容。
被跳过的内容不得对用户代理功能(例如页面内查找、Tab 键顺序导航等)可访问,也不得可被选中或聚焦。
注: 这类似于给内容赋予 display: none。
- auto
-
与 不同,被跳过的内容必须仍按正常方式供用户代理功能(如页面内查找、Tab 顺序导航等)使用,并必须能按正常方式进行聚焦和选择。
用户代理应该额外地为被跳过的内容尽可能避免布局/渲染工作;沉重的包含与使内容不可见且不可触摸的结合能够实现深度优化。如果在某一点进行了渲染工作,用户代理应尽可能保留先前计算的布局状态,以允许被跳过的内容在稍后时刻快速显示。
-
布局包含确保用户代理能够省略被跳过子树中的布局工作,因为此类布局的结果不会影响容器元素之外的元素。
-
样式包含确保在被跳过的子树中不需要处理计数器,因为它们不会影响容器元素之外的计数器。
-
渲染包含确保渲染内容的墨水溢出被剪裁;这反过来意味着用户代理可以可靠地确定元素的可见部分何时接近视口(并且对于 content-visibility: auto,开始渲染它)。
-
尺寸包含确保用户代理能够省略被跳过子树中的布局,因为此类布局的结果不会影响容器元素的大小。
注意,在 content-visibility: auto 的情况下,即使元素未被跳过,布局包含、样式包含和渲染包含仍然存在。这样做是为了防止因元素进入和退出被跳过状态而导致的包含变更所引起的布局更改。
4.1. 使用 content-visibility: hidden
本节是非规范性的。
content-visibility: hidden 对元素施加了强大的限制,因此应谨慎使用。它还实现了一些非常有用的场景,通常是对现有技术的改进,此处概述了其中一些。
-
如果页面需要对本身不会被渲染的元素或文本进行一些测量,通常这是通过将需要测量的东西定位在屏幕外来完成的,使用类似 position: absolute; left: -100000px; 的方法,然后调用诸如
getBoundingClientRect()之类的 API。不幸的是,即使页面从未打算显示此内容,用户代理仍然必须进行完整的样式计算、布局和渲染,以防它影响屏幕上显示的内容。作者在没有额外工作的情况下,也无法保证内容不会意外显示在屏幕上;即使是非常负的 left 值(如上所述)也可能不够,具体取决于内容。
将此内容包装在 content-visibility: hidden 容器中可以解决所有这些问题。如果包装器没有边框、背景等,那么它及其被跳过的内容保证永远不会在屏幕上渲染任何东西,无论它们变得多大。因为内容被跳过,用户代理也可以避免对它们进行样式计算或布局,直到脚本最终明确要求时为止。
-
“单页应用”通常由几个独立的窗格或“视图”组成,其中一次只显示一个。
如果作者想要避免为非活动视图支付样式/布局/渲染等成本,他们可以将它们完全从文档中删除,或者至少对它们应用 display:none。不幸的是,这意味着当视图确实需要显示时,所有的样式/布局/渲染等工作都必须一次性完成,这可能会导致视图实际出现之前出现明显的延迟。
或者,视图可以被定位在屏幕外。这意味着它在需要使用时会立即准备好,但它会一直产生样式/布局/渲染成本,这可能是显着的,特别是如果存在多个非活动视图。非活动视图也可能仍会显示在无障碍工具中,从而让屏幕阅读器用户、使用 Ctrl-F 查找页面内容的人等感到困惑。
content-visibility: hidden 对这两个选项都进行了改进。因为内容被跳过,所以当它们不活动时,用户代理不会在它们身上花费时间。它们对屏幕阅读器、页面查找和其他工具也是不可见的。而且因为用户代理应该尽可能保留先前的样式/布局工作,如果视图之前已经显示过,重新渲染它可能非常快。
-
如果作者想要使元素“不可见”,但仍显示在页面上以用于布局目的,一种选择是 visibility: hidden。但是,visibility: hidden 元素的后代可以设置 visibility: visible 并再次显示出来,这并不总是直观或预期的。
content-visibility: hidden 执行类似的目的,但后代不能将其“关闭”并开始显示;它们保持“隐藏”,直到祖先将其关闭。
因为 content-visibility: hidden 还对容器应用了许多包含值,所以它并不总是像 visibility: hidden 那样易于使用,但当它的限制是可以接受的时候,它可以成为一种更可靠、更一致的隐藏元素内容的方法。
4.2. 使用 content-visibility: auto
本节是非规范性的。
content-visibility: auto 是一个比 更复杂的值;与其类似于 display: none,它会自适应地隐藏/显示元素的内容,因为它们变得与用户相关。它也不会对用户代理隐藏其被跳过的内容,因此屏幕阅读器、页面内查找和其他工具仍然可以与之交互。
最好将其视为对包含的升级:如果作者有大量内容需要显示,而这些内容经常处于屏幕外(例如长可滚动列表),并且这些内容能够接受沉重的包含,他们应该考虑使用 content-visibility: auto 来一次性应用所有包含。这也强烈暗示用户代理可以跳过对内容的工作(这可能导致内容出现在屏幕上时会有微小的延迟),因为文档中包含大量内容并且大部分内容不会被看到更为重要。
注: 因此,content-visibility: auto 在许多情况下可以代替复杂的“虚拟列表”技术。
因为 content-visibility: auto 仅在内容均不与用户相关时才导致元素跳过其内容,因此最好以相当精细的粒度使用。
相反,应将 content-visibility 应用于个别推文,允许它们在处于屏幕外时被跳过。
因为当元素跳过其内容时,content-visibility: auto 会施加尺寸包含,如果元素依赖于其内容来确定其大小,那么当元素离开屏幕并开始跳过时,页面的布局(或者至少滚动条位置)可能会“乱跳”。
-
使元素具有固定大小
-
仔细安排诸如 Grid 之类的布局,以在不依赖其内容的情况下调整元素大小
-
使用 contain-intrinsic-size 设置元素的估计大小
-
使用 contain-intrinsic-size: auto 自动“快照”元素最后一次渲染时的精确大小,在其被跳过之前(同时提供在渲染之前和可以对其进行快照之前使用的估计大小)
例如,在 Twitter 上,平均推文高度约为 200px,因此 contain-intrinsic-size: auto 500px 200px 将确保即便在前面的或后面的推文被跳过时,滚动条滑块也处于近似正确的大小和位置,同时仍允许推文在处于屏幕上时根据其内容调整大小。只要推文至少被查看过一次(并且在它们被跳过时没有改变大小),它们的大小在被跳过时将是完全正确的,滚动条滑块也将如此;只有新加载的推文(例如在您向下滚动时加载到时间轴顶部的那些)才会被迫依赖于 200px 的高度估计。
4.3. 检测 content-visibility: auto 状态变化:contentvisibilityautostatechanged 事件
contentvisibilityautostatechanged 事件在渲染状态发生变化且元素变为或不再与用户相关时,在具有 content-visibility: auto 样式的元素上触发。
此事件通过在状态更改发生时发布任务来调度。
[Exposed =Window ]interface :ContentVisibilityAutoStateChangedEvent Event {(constructor DOMString ,type optional ContentVisibilityAutoStateChangedEventInit = {});eventInitDict readonly attribute boolean skipped ; };dictionary :ContentVisibilityAutoStateChangedEventInit EventInit {boolean skipped =false ; };
ContentVisibilityAutoStateChangedEvent 属性说明
请注意,在 content-visibility: auto 子树中的元素,即使对于跳过其内容的元素,也保持语义相关。这意味着使用此信号无限期地跳过被跳过子树中的 DOM 更新是不恰当的。相反,它应该用于降低更新的优先级,但确保内容保持语义相关且合理地更新。这对于即使在祖先设置为跳过其内容时也使用此内容的辅助技术尤为重要。
4.4. 限制与说明
-
从
IntersectionObserver的角度来看,元素的被跳过的内容从不与相交根相交。即使根元素和目标元素都在被跳过的内容中,此规则也成立。 -
从
ResizeObserver的角度来看,元素的被跳过的内容永远不会改变它们的大小。如果这些元素后来变得不被跳过,则如果新大小与用于通知 ResizeObserver 的最后一次大小不同,调整大小的观察结果将得到交付。 -
如果一个元素开始或停止跳过其内容,此变更将在渲染该变更效果的帧的 requestAnimationFrame 回调运行后发生。具体而言,此类变更将在处理模型的更新渲染 (Update the Rendering) 步骤的第 13 步和第 14 步之间生效(即在“运行动画帧回调”和“运行更新交叉观察步骤”之间)。
确定元素的视口交叉情况可以使用 IntersectionObserver 的内部版本来完成。然而,由于由此产生的观察结果是在“更新渲染”的第 14 步分发的,因此跳过(即已绘制)状态的任何变更直到下一帧的处理时用户才会可见。出于这个原因,更新跳过状态(包括包含关系调整)也被推迟到那一帧。这确保了脚本在访问(例如)这两个事件(内部交叉观察和跳过状态更新)之间的元素包含值时,将检索到与当前绘制状态一致的值,且不会导致任何强制布局。 -
对于 content-visibility: auto,初始可见性判定必须在确定存在新的 content-visibility: auto 元素的同一帧内完成。
当一个元素首次获得 content-visibility: auto 时,它可能位于屏幕内,也可能不在。对该状态的确定,以及由此确定该元素是否被跳过,必须在同一帧内完成。如果不这样做,则有可能在元素原本的位置产生空白内容,因为可见性检查和跳过状态更新会被推迟到下一帧。 -
就滚动操作而言,例如
scrollIntoView(),一个具有 content-visibility: auto 且正在跳过其内容的元素,其大小和位置的确定是在尺寸包含 (size containment) 仍然激活的情况下完成的。注:一旦滚动到视图中,该元素将不再跳过其内容,因此可能不再具有尺寸包含;如果这改变了元素的大小,它可能无法完全按照要求对齐到视口中。
如果一个元素对用户代理功能不可用(例如,如果由于存在 content-visibility: hidden 的祖先而被跳过),那么滚动操作根本不应滚动到该元素,就好像它没有布局框一样。
-
如果一个具有 content-visibility: auto 且正在跳过其内容的元素(或其内容)获得焦点,它会在由于聚焦而滚动到视图之前,变为与用户相关 (relevant to the user)(从而停止跳过其内容)。
注:因此,与前一点不同,该元素将会在视口中正确调整大小和对齐。这与
focus()方法的步骤顺序是一致的。 -
如果一个
iframe跳过其内容或者是一个元素被跳过内容的一部分,如果可能,用户代理应完全跳过 iframe 事件循环中的更新渲染步骤。 -
当一个元素被跳过时,该元素上的 CSS 过渡和动画不会更新。
-
即使新应用的样式会启动动画,也不会创建新的动画。
-
现有的动画不会在其时间线上推进。
-
元素上正在运行的动画不会结束。
如果脚本查询一个被跳过元素的样式(导致样式变更事件),且返回正确信息需要知道动画或过渡的状态,则动画和过渡会根据该样式变更事件时的样式进行采样。
CSS 动画 2 § 4 动画事件 和 CSS 过渡 2 § 5 过渡事件 定义了当动画或过渡更新时,创建哪些对象以及触发哪些事件,以及携带什么数据。
当一个元素停止被跳过时,动画和过渡会被采样,然后从该点开始正常恢复其时间线的推进。
注:总的来说,这类似于后台标签页被切换回前台时过渡/动画的行为,允许用户代理在元素变得相关时,在不过度中断动画的前提下,尽可能跳过不必要的动画工作。
-
-
当一个元素被跳过时,它不得启动任何过渡,即使样式变更事件影响了它的计算样式。
当一个元素停止被跳过时,它不得因为该元素不再被跳过所关联的样式变更事件而启动任何过渡。
注:这类似于元素从 display:none 切换到非 none 的值——即使在这种情况下样式实际上发生了改变(从初始值到级联中的“正确”值),也不会启动任何过渡。
-
如果一个元素拥有一个具有 content-visibility: hidden 的祖先,并且它被放置在顶层 (top layer) 中,它不会生成任何框,就好像它是 display: none 一样。
注:因其他原因(例如拥有一个 content-visibility: auto 祖先)而被跳过的元素仍将正常生成框,因此可能会变为非跳过状态。
4.5. 可访问性影响 (Accessibility Implications)
如果用户代理公开了某种“可访问性树”,类似于 DOM 树但专门用于可访问性用例(例如屏幕阅读器),从而提供可访问性 API 相关元素的位置等信息(如可聚焦元素),那么 content-visibility: hidden 元素的被跳过内容必须同样在可访问性树中被“跳过”(省略),类似于 display: none 元素在文档的所有视图中都被省略。
content-visibility: auto 元素的被跳过内容不得暴露用户是通过可访问性树与页面交互,而不是通过在屏幕上视觉渲染这一事实。特别是,如果用户代理使用 content-visibility: auto 来避免对屏幕外内容进行布局和绘制工作,它同样必须避免对屏幕外内容在可访问性树中的表示进行这些工作。如果这样做是不可能的(例如,如果用户代理在可访问性树中表示可聚焦元素时需要其精确位置,从而需要对该元素及其周围内容进行完整布局),那么用户代理必须完全从可访问性树中省略这些被跳过内容。
注:此要求旨在保护使用辅助工具的用户,防止其通过观察时间通道被识别和描述;如果用户代理在进行视觉渲染时可以跳过大量工作,但在渲染到可访问性树时必须完成所有工作,那么作者可以通过观察布局操作的时间来判断用户是如何与页面交互的。
4.6. 示例
< style > . sv { content-visibility : auto ; min-height : 50 px ; } </ style > < div class = sv > ... some content goes here ...</ div >
.sv 元素的 content-visibility: auto 值允许用户代理管理该元素是否被跳过。具体来说,当此元素靠近视口时,用户代理将开始绘制该元素。当元素远离视口时,它将停止被绘制。此外,当元素被跳过时,用户代理应尽可能跳过渲染工作。
< style > . sv { content-visibility : hidden ; } </ style > < div class = sv > ... some content goes here ...</ div >
在这种情况下,无论视口交叉情况如何,该元素都会被跳过。这意味着绘制内容的唯一方法是通过脚本更新值以移除 content-visibility 或改变其值。如前所述,用户代理应尽可能跳过内容中的渲染。
跳过渲染的一个额外影响是,内容的布局状态可以被用户代理保留,这样将来移除 content-visibility 属性时,内容的渲染速度会比使用 display: none 等方式隐藏时更快。
< style > body { margin : 0 ; } . sv { content-visibility : hidden ; position : relative ; left : 10 px ; top : 20 px ; } # child { position : relative ; left : 1 px ; top : 2 px ; width : 100 px ; height : 200 px ; } </ style > < div id = target class = sv > < div id = child ></ div > ... some other content goes here ...</ div > < script > ... // This will force rendering work, including layout, // if the UA previously avoided it. target. firstElementChild. getBoundingClientRect(); ... </ script >
与上一个例子类似,该元素被跳过。用户代理应尽可能避免渲染工作。然而,在这个例子中,在某个时刻,脚本访问了该元素内容中的布局值。在这种情况下,用户代理无法避免渲染工作,必须处理之前跳过的渲染工作,以便向调用者返回正确的值。在这个例子中,getBoundingClientRect() 的结果是一个位于 (11, 22)、大小为 100x200 的矩形。
请注意,对相同布局值的重复调用不应导致任何额外的渲染工作,因为用户代理应该保留最后更新的渲染状态。
还要注意,这种需要渲染工作的情况并不是唯一的。可能还存在其他用户代理无法避免渲染工作的情况。
5. 隐私注意事项 (Privacy Considerations)
已知此规范中的特性并未产生隐私影响。
6. 安全注意事项 (Security Considerations)
已知此规范中的功能并不存在安全影响。
像任何其他 CSS 规范一样,它会影响文档的渲染,但不会引入任何以前在其他 CSS 模块中无法实现,或格式化文档行为本身所固有的以误导方式呈现内容的特殊能力。
附录 A. 变更
本附录是信息性的。
自 2020-12-16 工作草案 以来的变更
-
在 strict 和 content 关键字中加入了样式包含 (style containment)。
-
在判断元素是否“在屏幕上”(作为确定其是否与用户相关的一部分)时,使用溢出剪裁边缘 (overflow clip edge) 而非边框边缘 (border edge)。
-
定义了动画和过渡如何作用于被跳过的元素。
-
从样式包含中移除了“有风险 (at risk)”标记。
-
当包含启用时,禁用来自 HTML body 元素的传播。
-
澄清了 scrollIntoView() 不会滚动到 content-visibility:hidden 元素的子元素。
-
定义了拥有 content-visibility: hidden 祖先的元素不会在顶层生成框。
-
定义了处于顶层使元素与用户相关。
-
指出具有非局部效果的绘制效果可能会限制某些优化机会。
-
添加了 ContentVisibilityAutoStateChanged 事件。
自 2020-06-03 工作草案 以来的变更
-
更改了 contain 属性的计算值确定方式。
-
修复了关于 contain: content 的注脚中的语法错误。
-
术语更改:将“containing box”替换为“containment box”(与在 Level 1 中所做的相同改进保持同步)。
-
对尺寸和绘制包含进行了编辑改进和澄清(与在 Level 1 中所做的相同改进保持同步)。
-
明确尺寸包含抑制自然宽高比(与在 Level 1 中所做的相同改进保持同步)。
-
将 content-visibility 的动画类型从离散更改为不可动画化。
-
在 § 4.4 限制和澄清 中添加了对 content-visibility: auto 的初始可见性判定时间的约束。
自 2019-11-11 工作草案 以来的变更
-
定义了绘制包含与 overflow-clip-margin 之间的交互。
-
添加了 content-visibility 属性。
自 CSS 包含级别 1 以来的变更
-
恢复了从级别 1 中删除的样式包含。