CSS 包含模块第 2 级

W3C 工作草案,

关于此文档的更多细节
此版本
https://w3org.cn/TR/2022/WD-css-contain-2-20220917/
最新发布版本
https://w3org.cn/TR/css-contain-2/
编辑草案
https://drafts.csswg.org/css-contain-2/
历史版本
历史
https://w3org.cn/standards/history/css-contain-2
测试套件
https://test.csswg.org/harness/results/css-contain-1_dev/
https://wpt.fyi/results/css/css-contain/
反馈
CSS 工作组问题仓库
编辑
Tab Atkins (Google)
Florian Rivoal (代表 Bloomberg)
(Google)
建议编辑此规范
GitHub 编辑器

摘要

本 CSS 模块描述了 contain 属性,该属性指示元素的子树与页面的其余部分无关。如果使用得当,这将允许用户代理进行深度优化。

CSS 是一种用于描述结构化文档(如 HTML 和 XML)在屏幕、纸张等介质上渲染方式的语言。

关于本文档

本部分描述了本文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新版本可在 W3C 技术报告索引(https://w3org.cn/TR/)中找到。

本文件由 CSS 工作组推荐标准轨道 (Recommendation track) 下的 工作草案 (Working Draft) 形式发布。以工作草案形式发布并不代表 W3C 及其成员的认可。

这是一份草案文档,可能会随时被其他文档更新、替换或废弃。将其作为非进行中工作引用是不恰当的。

请通过 在 GitHub 提交 Issue(首选)发送反馈,在标题中包含规范代号 “css-contain”,如:“[css-contain] …评论摘要…”。所有 Issue 和评论均 归档。也可将反馈发送至(归档)公共邮件列表 www-style@w3.org

本文档受 2021 年 11 月 2 日版 W3C 流程文档管辖。

本文档由在 W3C 专利政策下运作的组织制作。W3C 维护一份与该组织交付成果相关的 公开专利披露列表;该页面还包含披露专利的说明。任何知悉其认为包含 必要权利要求 的专利的个人,必须按照 W3C 专利政策第 6 节披露该信息。

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(无)
应用于 参见下方
可继承
百分比 不可用
计算值 关键字 nonesize, 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 元素。

contain 在页面上广泛使用时非常有用,特别是当页面包含许多相互独立的“小部件”时。

例如,假设一个微博社交网络有类似这样的标记

<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 htmlbody 元素上激活了任何包含时,属性从 body 元素到初始包含块、视口或画布背景的传播将被禁用。值得注意的是,这会影响

注: 属性传播到初始包含块、视口或画布背景,如果是在 html 元素本身设置的,则不受影响。

注: 除了 contain 之外,还有几个属性可以为元素开启各种包含。这些不会影响 contain 的值;例如,一个元素可以有 contain: none,但仍然可以通过 content-visibility 开启布局包含

3. 封装类型

有几种包含方式可应用于元素,以各种方式限制其后代对页面其余部分产生的影响。包含使用户代理能够进行更强大的优化,并帮助作者用功能单元构建页面,因为它限制了特定更改对文档的影响范围。

引入新属性或机制的规范作者需要考虑各种类型的包含是否以及如何影响他们所引入的内容,并在其规范中包含此处未描述的任何影响。

3.1. 尺寸包含

赋予元素尺寸包含使其主体框成为尺寸包含框,并具有以下效果

  1. 尺寸包含框固有尺寸的确定方式就好像该元素没有内容一样,遵循与视为空时调整大小相同的逻辑。

    注: 这会影响对 min-contentmax-content 关键字的显式调用,以及任何依赖于这些测量的计算,例如将尺寸包含项目放入其中时调整网格轨道的大小,或者如果调整包含框父级 fit-content 大小

  2. 尺寸包含框及其内容进行布局在概念上分为两个阶段

    视为空时调整大小
    使用中的 widthheight 的确定方式就好像执行框的正常布局一样,只是它被视为没有内容——甚至不通过伪元素(例如 ::before, ::after, 或 ::marker)包含任何内容。

    替换元素必须被视为具有 0 的固有宽度和高度,且没有固有纵横比

    注: 尺寸包含仅抑制固有纵横比,因此会尊重直接影响该首选纵横比的属性(例如 aspect-ratio)。

    尺寸包含框的所有 CSS 属性都会像正常执行布局时那样被考虑在内。其他规范可能会做出特定的豁免。

    注: 即使元素的尺寸属性指定了固有尺寸,这也不一定使元素尺寸为零:元素本身设置的属性会继续被考虑在内,这可能导致其变大。

    原地布局
    然后,包含框的内容(包括任何伪元素)必须正常地布局到现已固定尺寸的包含框中。

    注: 尺寸包含不会抑制基线对齐。请参见布局包含的相关内容。

  3. 尺寸包含框单体式的(参见 CSS 分片 3 § 4.1 可能的断点)。

给定以下标记和样式,图像将被调整为 100px x 100px,因为由 aspect-ratio 属性设置的纵横比生效。
img {
  width: 100px;
  aspect-ratio: 1/1;
  contain: size;
}
<img src="https://www.example.com/300x100.jpg">

如果未声明 aspect-ratio 属性,图像将为 100px x 0px,因为其固有纵横比被抑制,且其固有高度被视为 0。

但是,如果满足以下任何条件,则为元素赋予尺寸包含无效

注: 不包括表格标题在内的内部表格框被排除在外,因为表格布局算法不允许框变得比其流入内容更小。将表格单元格调整为好像它为空,然后在不改变大小的情况下在其内部布局其内容,实际上是一个未定义的操作。手动将 widthheight 属性设置为 0 无法使其比其内容更小。此关注点不适用于表格标题,表格标题完全可以具有独立于其内容的固定大小。

3.1.1. 可能的尺寸包含优化

本节是非规范性的。

单独使用尺寸包含本身并不能提供太多优化机会。它自身的主要好处在于,想要基于包含框的大小来布局包含框内容的工具(例如实现“容器查询”概念的 JS 库)可以在不用担心“无限循环”的情况下进行布局,即子项的大小响应包含框的大小导致包含框的大小也发生变化,可能触发进一步的子项调整大小的方式改变,从而可能导致包含框大小更多的变化,以至无穷。

但是,当与布局包含搭配使用时,可以启用的优化包括(但不限于)

  1. 包含框的后代的样式或内容发生变化时,计算 DOM 树中哪些部分是“脏的”且可能需要重新布局的操作可以在包含框处停止。

  2. 在布局页面时,如果包含框在屏幕外或被遮挡,其内容的布局(即“原地布局”)可以延迟或以较低优先级执行。

3.2. 布局包含

赋予元素布局包含使其主体框成为布局包含框,并具有以下效果

  1. 布局包含框建立一个独立的格式化上下文

  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

  3. 如果 overflow 属性的计算值是 visibleclip 或其组合,则任何溢出都必须视为墨水溢出

  4. 布局包含框建立一个绝对定位包含块和一个固定定位包含块

  5. 布局包含框创建一个堆叠上下文

  6. 强制断点允许在布局包含框内,但不会像 CSS 分片 3 § 3.1 框之间的断点:break-before 和 break-after 属性中所述那样传播到父级。

    注: 这引入了以前不存在的可能性,即强制断点可能发生在框及其容器之间(参见 CSS 分片 3 § 4.1 可能的断点)。

  7. 对于 vertical-align 属性,或任何其他需要将布局包含框的基线位置与除其后代之外的事物相关联的属性,包含框被视为没有基线。

但是,如果满足以下任何条件,则为元素赋予布局包含无效

3.2.1. 可能的布局包含优化

本节是非规范性的。

可以由布局包含启用的优化包括(但不限于)

  1. 在布局页面时,单独的包含框的内容可以并行布局,因为它们保证不会相互影响。

  2. 在布局页面时,如果包含框在屏幕外或被遮挡,并且屏幕可见部分的布局不依赖于包含框的大小(例如,如果包含框靠近块容器的末尾,而您正在查看块容器的开头),包含框内容的布局可以延迟或以较低优先级执行。

    (当与尺寸包含搭配使用时,此优化可以更自由地应用。)

3.3. 样式包含

赋予元素样式包含具有以下效果

  1. counter-incrementcounter-set 属性必须作用域限定为元素的子树并创建一个新的计数器。

  2. content 属性的 open-quote, close-quote, no-open-quoteno-close-quote 的效果必须作用域限定为元素的子树

    注: 这意味着子树中引号嵌套的深度保持不变,并从其上下文通常暗示的值开始,但是子树内部这些值对引号嵌套深度的更改不会影响子树外部的引号嵌套深度。

注: [CSS-REGIONS-1]样式包含如何影响区域有规范性要求。

作用域属性将其效果限定为特定元素或子树。

由于 counter-increment 限定作用域为元素的子树,因此在子树内第一次使用它时,表现得就像在作用域元素上将命名计数器设置为 0,无论该计数器是否已在作用域元素外部使用。在子树内进行的任何递增对作用域元素外部同名的计数器没有影响。但是,content 属性的 counter()counters() 值本身并不限定作用域,并且可以引用在子树外部建立的计数器。因此,以下代码导致显示 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. 可能的样式包含优化

本节是非规范性的。

可以由样式包含启用的优化包括(但不限于)

  1. 每当具有样式包含的元素的后代上的属性发生变化时,计算 DOM 树中哪些部分是“脏的”且可能需要重新计算样式的操作可以在具有样式包含的元素处停止。

3.4. 渲染包含

赋予元素渲染包含使其主体框成为渲染包含框,并具有以下效果

  1. 元素的内容(包括任何墨水溢出可滚动溢出)必须剪裁到渲染包含框溢出剪裁边缘,同时考虑 [[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-xoverflow-y 的其他值不变。

  2. 渲染包含框建立一个绝对定位包含块和一个固定定位包含块

  3. 渲染包含框创建一个堆叠上下文

  4. 渲染包含框建立一个独立的格式化上下文

但是,如果满足以下任何条件,则为元素赋予渲染包含无效

3.4.1. 可能的渲染包含优化

本节是非规范性的。

可以由渲染包含启用的优化包括(但不限于)

  1. 如果包含框在屏幕外或被遮挡,UA 通常可以跳过对其内容进行渲染的尝试,因为它们也保证在屏幕外/被遮挡。

    注: 某些渲染效果(例如 [FILTER-EFFECTS-1] 中的 blur() 滤镜)具有非局部影响。用户代理需要跟踪这些影响,因为它可能需要在具有此类滤镜的元素的后代发生变化时重新渲染元素的部分,即使它们具有渲染包含且本可以跳过也是如此。

  2. 除非被剪裁的内容可以通过其他机制(例如 overflow, resize, 或 text-overflow 属性)使之可访问,否则 UA 可以为该框保留正好等于该框大小的“画布”空间。(在类似的可滚动情况下,例如 overflow: hidden,可以滚动到当前被剪裁的内容,因此 UA 通常会预见性地过度渲染一些,以便在滚动发生时立即有内容可见,而不是一帧之后才显示。)

  3. 因为保证它们是堆叠上下文,可滚动的元素可以被渲染到单个 GPU 层中。

4. 完全抑制元素的内容:content-visibility 属性

名称content-visibility
visible | auto | hidden
初始值 visible
应用于 可以应用布局包含的元素
可继承
百分比 不可用
计算值 按指定值
规范顺序 按语法
动画类型 不可动画

content-visibility 属性控制元素是否渲染其内容,并强制执行一组强有力的包含,允许用户代理在需要之前潜在地省略大量的布局和渲染工作。它有以下值

visible

无效果。元素的的内容按正常方式布局和渲染。

该组件可以在本包或其他任何使用包的其他组件中被引用;它不能在使用包中被不同的组件覆盖。

元素跳过其内容

被跳过的内容不得对用户代理功能(例如页面内查找、Tab 键顺序导航等)可访问,也不得可被选中或聚焦。

注: 这类似于给内容赋予 display: none

auto

为该元素开启布局包含样式包含渲染包含

如果元素与用户不相关,它也会跳过其内容

hidden 不同,被跳过的内容必须仍按正常方式供用户代理功能(如页面内查找、Tab 顺序导航等)使用,并必须能按正常方式进行聚焦和选择。

当元素跳过其内容时,用户代理必须更改 contain 属性的使用值,以开启布局包含样式包含渲染包含尺寸包含。此外,其内容(该元素的扁平树后代,包括文本和元素,或替换元素的替换内容)不会被渲染(就像它们具有 visibility: hidden 一样),并且不响应点击测试(就像它们具有 pointer-events: none 一样),并且在很大程度上除非脚本显式请求(详情请参见 § 4.4 限制与说明),否则根本不会更新它们的样式。

用户代理应该额外地为被跳过的内容尽可能避免布局/渲染工作;沉重的包含与使内容不可见且不可触摸的结合能够实现深度优化。如果在某一点进行了渲染工作,用户代理应尽可能保留先前计算的布局状态,以允许被跳过的内容在稍后时刻快速显示。

如果元素的值不是 content-visibility: visible,则满足以下属性
  • 布局包含确保用户代理能够省略被跳过子树中的布局工作,因为此类布局的结果不会影响容器元素之外的元素。

  • 样式包含确保在被跳过的子树中不需要处理计数器,因为它们不会影响容器元素之外的计数器。

  • 渲染包含确保渲染内容的墨水溢出被剪裁;这反过来意味着用户代理可以可靠地确定元素的可见部分何时接近视口(并且对于 content-visibility: auto,开始渲染它)。

  • 尺寸包含确保用户代理能够省略被跳过子树中的布局,因为此类布局的结果不会影响容器元素的大小。

注意,在 content-visibility: auto 的情况下,即使元素未被跳过布局包含样式包含渲染包含仍然存在。这样做是为了防止因元素进入和退出被跳过状态而导致的包含变更所引起的布局更改。

如果满足以下任一条件,则元素与用户相关

4.1. 使用 content-visibility: hidden

本节是非规范性的。

content-visibility: hidden 对元素施加了强大的限制,因此应谨慎使用。它还实现了一些非常有用的场景,通常是对现有技术的改进,此处概述了其中一些。

  1. 如果页面需要对本身不会被渲染的元素或文本进行一些测量,通常这是通过将需要测量的东西定位在屏幕外来完成的,使用类似 position: absolute; left: -100000px; 的方法,然后调用诸如 getBoundingClientRect() 之类的 API。

    不幸的是,即使页面从未打算显示此内容,用户代理仍然必须进行完整的样式计算、布局和渲染,以防它影响屏幕上显示的内容。作者在没有额外工作的情况下,也无法保证内容不会意外显示在屏幕上;即使是非常负的 left 值(如上所述)也可能不够,具体取决于内容。

    将此内容包装在 content-visibility: hidden 容器中可以解决所有这些问题。如果包装器没有边框、背景等,那么它及其被跳过的内容保证永远不会在屏幕上渲染任何东西,无论它们变得多大。因为内容被跳过,用户代理也可以避免对它们进行样式计算或布局,直到脚本最终明确要求时为止。

  2. “单页应用”通常由几个独立的窗格或“视图”组成,其中一次只显示一个。

    如果作者想要避免为非活动视图支付样式/布局/渲染等成本,他们可以将它们完全从文档中删除,或者至少对它们应用 display:none。不幸的是,这意味着当视图确实需要显示时,所有的样式/布局/渲染等工作都必须一次性完成,这可能会导致视图实际出现之前出现明显的延迟。

    或者,视图可以被定位在屏幕外。这意味着它在需要使用时会立即准备好,但它会一直产生样式/布局/渲染成本,这可能是显着的,特别是如果存在多个非活动视图。非活动视图也可能仍会显示在无障碍工具中,从而让屏幕阅读器用户、使用 Ctrl-F 查找页面内容的人等感到困惑。

    content-visibility: hidden 对这两个选项都进行了改进。因为内容被跳过,所以当它们不活动时,用户代理不会在它们身上花费时间。它们对屏幕阅读器、页面查找和其他工具也是不可见的。而且因为用户代理应该尽可能保留先前的样式/布局工作,如果视图之前已经显示过,重新渲染它可能非常快。

  3. 如果作者想要使元素“不可见”,但仍显示在页面上以用于布局目的,一种选择是 visibility: hidden。但是,visibility: hidden 元素的后代可以设置 visibility: visible 并再次显示出来,这并不总是直观或预期的。

    content-visibility: hidden 执行类似的目的,但后代不能将其“关闭”并开始显示;它们保持“隐藏”,直到祖先将其关闭。

    因为 content-visibility: hidden 还对容器应用了许多包含值,所以它并不总是像 visibility: hidden 那样易于使用,但当它的限制是可以接受的时候,它可以成为一种更可靠、更一致的隐藏元素内容的方法。

4.2. 使用 content-visibility: auto

本节是非规范性的。

content-visibility: auto 是一个比 hidden 更复杂的值;与其类似于 display: none,它会自适应地隐藏/显示元素的内容,因为它们变得与用户相关。它也不会对用户代理隐藏其被跳过的内容,因此屏幕阅读器、页面内查找和其他工具仍然可以与之交互。

最好将其视为包含的升级:如果作者有大量内容需要显示,而这些内容经常处于屏幕外(例如长可滚动列表),并且这些内容能够接受沉重的包含,他们应该考虑使用 content-visibility: auto 来一次性应用所有包含。这也强烈暗示用户代理可以跳过对内容的工作(这可能导致内容出现在屏幕上时会有微小的延迟),因为文档中包含大量内容并且大部分内容不会被看到更为重要。

注: 因此,content-visibility: auto 在许多情况下可以代替复杂的“虚拟列表”技术。

因为 content-visibility: auto 仅在内容均不与用户相关时才导致元素跳过其内容,因此最好以相当精细的粒度使用。

例如,在 Twitter 上,对整个时间轴应用 content-visibility: auto 不会完成太多工作——它总是在屏幕上,所以它永远不会跳过其内容

相反,应将 content-visibility 应用于个别推文,允许它们在处于屏幕外时被跳过

因为当元素跳过其内容时,content-visibility: 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 属性说明

skipped, 类型 boolean, 只读

如果目标将状态更改为跳过其内容,则设置为 true,否则为 false。

ContentVisibilityAutoStateChangedEventInit 成员说明

skipped, 类型 boolean, 默认 false

参见 skipped 属性的说明。

请注意,在 content-visibility: auto 子树中的元素,即使对于跳过其内容的元素,也保持语义相关。这意味着使用此信号无限期地跳过被跳过子树中的 DOM 更新是不恰当的。相反,它应该用于降低更新的优先级,但确保内容保持语义相关且合理地更新。这对于即使在祖先设置为跳过其内容时也使用此内容的辅助技术尤为重要。

4.4. 限制与说明

  1. IntersectionObserver 的角度来看,元素的被跳过的内容从不与相交根相交。即使根元素和目标元素都在被跳过的内容中,此规则也成立。

  2. ResizeObserver 的角度来看,元素的被跳过的内容永远不会改变它们的大小。如果这些元素后来变得不被跳过,则如果新大小与用于通知 ResizeObserver 的最后一次大小不同,调整大小的观察结果将得到交付。

  3. 如果一个元素开始或停止跳过其内容,此变更将在渲染该变更效果的帧的 requestAnimationFrame 回调运行后发生。具体而言,此类变更将在处理模型的更新渲染 (Update the Rendering) 步骤的第 13 步和第 14 步之间生效(即在“运行动画帧回调”和“运行更新交叉观察步骤”之间)。

    确定元素的视口交叉情况可以使用 IntersectionObserver 的内部版本来完成。然而,由于由此产生的观察结果是在“更新渲染”的第 14 步分发的,因此跳过(即已绘制)状态的任何变更直到下一帧的处理时用户才会可见。出于这个原因,更新跳过状态(包括包含关系调整)也被推迟到那一帧。这确保了脚本在访问(例如)这两个事件(内部交叉观察和跳过状态更新)之间的元素包含值时,将检索到与当前绘制状态一致的值,且不会导致任何强制布局。
  4. 对于 content-visibility: auto,初始可见性判定必须在确定存在新的 content-visibility: auto 元素的同一帧内完成。

    当一个元素首次获得 content-visibility: auto 时,它可能位于屏幕内,也可能不在。对该状态的确定,以及由此确定该元素是否被跳过,必须在同一帧内完成。如果不这样做,则有可能在元素原本的位置产生空白内容,因为可见性检查和跳过状态更新会被推迟到下一帧。
  5. 就滚动操作而言,例如 scrollIntoView(),一个具有 content-visibility: auto 且正在跳过其内容的元素,其大小和位置的确定是在尺寸包含 (size containment) 仍然激活的情况下完成的。

    注:一旦滚动到视图中,该元素将不再跳过其内容,因此可能不再具有尺寸包含;如果这改变了元素的大小,它可能无法完全按照要求对齐到视口中。

    如果一个元素对用户代理功能不可用(例如,如果由于存在 content-visibility: hidden 的祖先而被跳过),那么滚动操作根本不应滚动到该元素,就好像它没有布局框一样。

  6. 如果一个具有 content-visibility: auto 且正在跳过其内容的元素(或其内容)获得焦点,它会在由于聚焦而滚动到视图之前,变为与用户相关 (relevant to the user)(从而停止跳过其内容)。

    注:因此,与前一点不同,该元素将会在视口中正确调整大小和对齐。这与 focus() 方法的步骤顺序是一致的。

  7. 如果一个 iframe 跳过其内容或者是一个元素被跳过内容的一部分,如果可能,用户代理应完全跳过 iframe 事件循环中的更新渲染步骤。

    注:iframe 开始被跳过的时刻,它需要至少运行一次该步骤以移除绘制的输出。

  8. 被跳过的内容不计入 innerText 的结果。

  9. 当一个元素被跳过时,该元素上的 CSS 过渡和动画不会更新。

    • 即使新应用的样式会启动动画,也不会创建新的动画。

    • 现有的动画不会在其时间线上推进。

    • 元素上正在运行的动画不会结束。

    如果脚本查询一个被跳过元素的样式(导致样式变更事件),且返回正确信息需要知道动画或过渡的状态,则动画和过渡会根据该样式变更事件时的样式进行采样。

    CSS 动画 2 § 4 动画事件CSS 过渡 2 § 5 过渡事件 定义了当动画或过渡更新时,创建哪些对象以及触发哪些事件,以及携带什么数据。

    当一个元素停止被跳过时,动画和过渡会被采样,然后从该点开始正常恢复其时间线的推进。

    注:总的来说,这类似于后台标签页被切换回前台时过渡/动画的行为,允许用户代理在元素变得相关时,在不过度中断动画的前提下,尽可能跳过不必要的动画工作。

  10. 当一个元素被跳过时,它不得启动任何过渡,即使样式变更事件影响了它的计算样式。

    当一个元素停止被跳过时,它不得因为该元素不再被跳过所关联的样式变更事件而启动任何过渡。

    注:这类似于元素从 display:none 切换到非 none 的值——即使在这种情况下样式实际上发生了改变(从初始值到级联中的“正确”值),也不会启动任何过渡。

  11. 如果一个元素拥有一个具有 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: 50px;
}
</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: 10px;
  top: 20px;
}
#child {
  position: relative;
  left: 1px;
  top: 2px;
  width: 100px;
  height: 200px;
}
</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 工作草案 以来的变更

2020-06-03 工作草案 以来的变更

2019-11-11 工作草案 以来的变更

CSS 包含级别 1 以来的变更

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语相结合来表达。本文档规范性部分中的关键词“MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(必需)、“SHALL”(应)、“SHALL NOT”(不应)、“SHOULD”(推荐)、“SHOULD NOT”(不推荐)、“RECOMMENDED”(建议)、“MAY”(可以)和“OPTIONAL”(可选)应按照 RFC 2119 中的描述进行解释。然而,为了可读性,这些词在本文档中不以全大写形式出现。

本规范的所有文本均为规范性文本,明确标记为非规范性、示例和注释的章节除外。[RFC2119]

本规范中的示例均以“例如”一词引入,或者通过 class="example" 与规范性文本隔开,如下所示

这是一个说明性示例。

说明性注释以“Note”一词开头,并使用 class="note" 与规范性文本隔开,如下所示

注意,这是一个说明性注释。

建议(Advisements)是规范性章节,旨在引起特别注意,并使用 <strong class="advisement"> 与其他规范性文本区分开来,如下所示: 用户代理必须提供可访问的替代方案。

一致性类别

本规范为三类一致性定义了一致性要求。

样式表
一份 CSS 样式表
渲染器
一种 用户代理 (UA),它解释样式表的语义并渲染使用它们的文档。
创作工具
一种 用户代理 (UA),用于编写样式表。

如果样式表包含的所有使用本模块定义语法的语句,根据通用 CSS 语法及本模块定义的各功能语法均有效,则该样式表符合本规范。

如果渲染器除了按相应规范解释样式表外,还通过正确解析本规范定义的所有功能并相应地渲染文档来支持这些功能,则该渲染器符合本规范。然而,由于设备限制导致 UA 无法正确渲染文档,并不意味着该 UA 不符合规范。(例如,UA 无需在单色显示器上渲染颜色。)

如果创作工具编写的样式表根据通用 CSS 语法及本模块中各功能的语法是句法正确的,并符合本模块中描述的所有其他样式表一致性要求,则该创作工具符合本规范。

部分实现

为了使作者能够利用前向兼容的解析规则来指定后备值,CSS 渲染器 **必须** 将其无法使用支持级别的任何 @规则、属性、属性值、关键字和其他语法结构视为无效(并 适当忽略)。特别地,用户代理 **不得** 在单一多值属性声明中选择性地忽略不支持的组件值而保留支持的值:如果任何值被视为无效(因为不支持的值必须如此),CSS 要求忽略整个声明。

不稳定和专有特性的实现

为了避免与未来稳定的 CSS 功能发生冲突,CSS 工作组建议在实施不稳定功能和私有扩展遵循最佳实践

非实验性实现

一旦规范达到候选推荐阶段,非实验性实现即可成为可能,实现者应发布他们能够证明根据规范正确实现的任何 CR 级别特性的无前缀实现。

为建立并保持 CSS 在不同实现间的互操作性,CSS 工作组请求非实验性的 CSS 渲染器在发布任何 CSS 功能的无前缀实现之前,向 W3C 提交一份实现报告(并在必要时提交用于该实现报告的测试用例)。提交给 W3C 的测试用例需经 CSS 工作组审阅和修正。

有关提交测试用例和实现报告的详细信息,请访问 CSS 工作组网站 https://w3org.cn/Style/CSS/Test/。问题可发送至 public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

通过引用定义的术语

引用

规范性引用

[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2021年7月26日. CR. URL: https://w3org.cn/TR/css-backgrounds-3/
[CSS-BOX-4]
Elika Etemad. CSS 盒模型模块 Level 4. 2020年4月21日. WD. URL: https://w3org.cn/TR/css-box-4/
[CSS-BREAK-3]
Rossen Atanassov; Elika Etemad. CSS Fragmentation Module Level 3. 2018年12月4日. CR. URL: https://w3org.cn/TR/css-break-3/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2022年1月13日. CR. URL: https://w3org.cn/TR/css-cascade-5/
[CSS-CONTAIN-1]
Tab Atkins Jr.; Florian Rivoal. CSS Containment Module Level 1. 2020年12月22日. REC. URL: https://w3org.cn/TR/css-contain-1/
[CSS-CONTENT-3]
Elika Etemad; Dave Cramer. CSS Generated Content Module Level 3. 2019年8月2日. WD. URL: https://w3org.cn/TR/css-content-3/
[CSS-DISPLAY-3]
Tab Atkins Jr.; Elika Etemad. CSS Display Module Level 3. 2021年9月3日. CR. URL: https://w3org.cn/TR/css-display-3/
[CSS-IMAGES-3]
Tab Atkins Jr.; Elika Etemad; Lea Verou. CSS 图像模块第 3 级. 2020年12月17日. CR. URL: https://w3org.cn/TR/css-images-3/
[CSS-INLINE-3]
Dave Cramer; Elika Etemad; Steve Zilles. CSS Inline Layout Module Level 3. 2020年8月27日. WD. URL: https://w3org.cn/TR/css-inline-3/
[CSS-LISTS-3]
Elika Etemad; Tab Atkins Jr.. CSS Lists and Counters Module Level 3. 2020年11月17日. WD. URL: https://w3org.cn/TR/css-lists-3/
[CSS-OVERFLOW-3]
David Baron; Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 2021年12月23日. WD. URL: https://w3org.cn/TR/css-overflow-3/
[CSS-POSITION-3]
Elika Etemad; Tab Atkins Jr.. CSS Positioned Layout Module Level 3. 2022年9月1日. WD. URL: https://w3org.cn/TR/css-position-3/
[CSS-PSEUDO-4]
Daniel Glazman; Elika Etemad; Alan Stearns. CSS Pseudo-Elements Module Level 4. 2020年12月31日. WD. URL: https://w3org.cn/TR/css-pseudo-4/
[CSS-SCOPING-1]
Tab Atkins Jr.; Elika Etemad. CSS Scoping Module Level 1. 2014年4月3日. WD. URL: https://w3org.cn/TR/css-scoping-1/
[CSS-SIZING-3]
Tab Atkins Jr.; Elika Etemad. CSS Box Sizing Module Level 3. 2021年12月17日. WD. URL: https://w3org.cn/TR/css-sizing-3/
[CSS-TRANSITIONS-1]
David Baron; et al. CSS Transitions. 2018年10月11日. WD. URL: https://w3org.cn/TR/css-transitions-1/
[CSS-UI-3]
Tantek Çelik; Florian Rivoal. CSS 基础用户界面模块 Level 3 (CSS3 UI)。2018 年 6 月 21 日。REC。URL: https://w3org.cn/TR/css-ui-3/
[CSS-UI-4]
Florian Rivoal. CSS Basic User Interface Module Level 4. 2021年3月16日. WD. URL: https://w3org.cn/TR/css-ui-4/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS 值与单位模块 Level 3. 2019 年 6 月 6 日. CR. URL: https://w3org.cn/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值与单位模块 Level 4. 2021 年 12 月 16 日. WD. URL: https://w3org.cn/TR/css-values-4/
[CSS-WRITING-MODES-3]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 3. 2019年12月10日. REC. URL: https://w3org.cn/TR/css-writing-modes-3/
[CSS-WRITING-MODES-4]
Elika Etemad; Koji Ishii. CSS Writing Modes Level 4. 2019年7月30日. CR. URL: https://w3org.cn/TR/css-writing-modes-4/
[CSS2]
Bert Bos; et al. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011年6月7日. REC. URL: https://w3org.cn/TR/CSS21/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM 视图模块. 2016 年 3 月 17 日. WD. URL: https://w3org.cn/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM 标准. Living Standard. URL: https://dom.spec.whatwg.org/
[FULLSCREEN]
Philip Jägenstedt. 全屏 API 标准。现行标准。URL: https://fullscreen.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 标准. Living Standard. URL: https://html.whatwg.cn/multipage/
[INTERSECTION-OBSERVER]
Stefan Zager; Emilio Cobos Álvarez. Intersection Observer (交叉观察器). 2022年7月6日. WD. URL: https://w3org.cn/TR/intersection-observer/
[RESIZE-OBSERVER-1]
Aleks Totic; Greg Whitworth. 尺寸调整观察者 (Resize Observer). 2020年2月11日. WD. URL: https://w3org.cn/TR/resize-observer-1/
[RFC2119]
S. Bradner. RFC 中用于指示要求级别的关键词. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SVG2]
Amelia Bellamy-Royds; et al. Scalable Vector Graphics (SVG) 2. 2018年10月4日. CR. URL: https://w3org.cn/TR/SVG2/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. URL: https://webidl.spec.whatwg.org/

参考资料

[CSS-GRID-2]
Tab Atkins Jr.; Elika Etemad; Rossen Atanassov. CSS Grid Layout Module Level 2. 2020年12月18日. CR. URL: https://w3org.cn/TR/css-grid-2/
[CSS-MULTICOL-1]
Florian Rivoal; Rachel Andrew. CSS Multi-column Layout Module Level 1. 2021年10月12日. CR. URL: https://w3org.cn/TR/css-multicol-1/
[CSS-OVERFLOW-4]
David Baron; Florian Rivoal. CSS Overflow Module Level 4 (CSS 溢出模块级别 4). 2017年6月13日. WD. URL: https://w3org.cn/TR/css-overflow-4/
[CSS-PAGE-3]
Elika Etemad; Simon Sapin. CSS 分页媒体模块 Level 3. 2018 年 10 月 18 日. WD. URL: https://w3org.cn/TR/css-page-3/
[CSS-REGIONS-1]
Rossen Atanassov; Alan Stearns. CSS Regions Module Level 1. 2014年10月9日. WD. URL: https://w3org.cn/TR/css-regions-1/
[CSS-SIZING-4]
Tab Atkins Jr.; Elika Etemad; Jen Simmons. CSS 盒模型尺寸模块第 4 级. 2021年5月20日. WD. URL: https://w3org.cn/TR/css-sizing-4/
[FILTER-EFFECTS-1]
Dirk Schulze; Dean Jackson. 滤镜效果模块 1 级 (Filter Effects Module Level 1). 2018年12月18日. WD. URL: https://w3org.cn/TR/filter-effects-1/

属性索引

名称初始值应用于继承百分比动画类型 (Animation type)规范顺序计算值
contain none | strict | content | [ size || layout || style || paint ]none(无)见下文不可用不可动画按语法关键字 none 或 size、layout、paint 中的一个或多个
content-visibility visible | auto | hiddenvisible可应用布局包含的元素不可用不可动画按语法按指定值

IDL 索引

[Exposed=Window]
interface ContentVisibilityAutoStateChangedEvent : Event {
  constructor(DOMString type, optional ContentVisibilityAutoStateChangedEventInit eventInitDict = {});
  readonly attribute boolean skipped;
};
dictionary ContentVisibilityAutoStateChangedEventInit : EventInit {
  boolean skipped = false;
};