使用 CSS 包含

CSS 包含通过允许浏览器将页面的子树与页面的其余部分隔离开来,从而提高网页的性能。如果浏览器知道页面的某个部分独立于其余内容,则可以优化渲染并提高性能。

containcontent-visibility 属性使开发人员能够告知用户代理是否应该完全渲染元素的内容,以及是否应该在元素处于屏幕外时渲染其内容。用户代理随后将在适当情况下将包含应用于元素,可能推迟布局和渲染,直到需要为止。

本指南描述了 CSS 包含的基本目标以及如何利用 containcontent-visibility 来获得更好的用户体验。

基本示例

网页通常包含多个部分,从逻辑上讲,这些部分彼此独立。CSS 包含使它们在渲染方面真正彼此独立。

例如,博客通常包含几篇文章,每篇文章都包含标题和内容,如下面的标记所示。

html
<h1>My blog</h1>
<article>
  <h2>Heading of a nice article</h2>
  <p>Content here.</p>
</article>
<article>
  <h2>Another heading of another article</h2>
  <p>More content here.</p>
</article>

使用 CSS,我们使用 contain 属性的值为 content 应用于每篇文章。content 值是 contain: layout paint style 的简写

css
article {
  contain: content;
}

从逻辑上讲,每篇文章都独立于页面上的其他文章。此信息通常是创建页面的 Web 开发人员已知的,并且可能非常明显。但是,浏览器不知道您的内容的意图,不能假设文章或其他内容部分将完全独立。

此属性提供了一种向浏览器解释这一点并明确授权其进行性能优化的方式。它告诉浏览器元素的内部布局与页面的其余部分完全分开,并且元素的所有内容都在其边界内绘制。没有任何内容可以明显溢出。

通过在每个 <article> 上设置 contain: content,我们已经指明了这一点;我们已经告诉浏览器每个文章都是独立的。然后,浏览器可以使用此信息来决定如何渲染每个 <article> 内容。例如,它可能不会渲染不在可视区域的文章。

当在页面末尾追加更多文章时,浏览器不需要重新计算布局或重新绘制前面的内容;它也不需要触碰包含元素子树之外的任何区域。但是,如果盒子模型属性依赖,则浏览器将需要重新计算布局和重新绘制。例如,如果 <article> 的样式设置使其大小取决于其内容(例如,使用 height: auto),则浏览器将需要考虑其大小变化。

关键概念和术语

contain

有四种类型的包含:布局、绘制、大小和样式。使用 contain 属性通过包含这些类型的任何组合来指定要应用于元素的类型或类型。

布局包含

css
article {
  contain: layout;
}

布局通常作用于整个文档,这意味着如果你移动一个元素,整个文档都需要被视为元素可能移动到任何地方。通过使用 `contain: layout`,你可以告诉浏览器它只需要检查这个元素——元素内部的所有内容都作用于该元素,并且不影响页面其他部分,包含框建立一个独立的 格式化上下文

此外

  • `float` 布局将在指定元素内部独立执行。
  • 边距不会跨越布局包含边界折叠。
  • 布局容器是 包含块,用于 `absolute` 和 `fixed` 定位的子元素。
  • 包含框创建一个 堆叠上下文,因此可以 使用 `z-index`

注意: 使用 `container-type``container-name` 属性时,将自动应用 `contain` 的 `style` 和 `layout` 值。

绘制包含

css
article {
  contain: paint;
}

绘制包含本质上将框剪裁到 主要框 的填充边缘。不能有任何可见的溢出。对于 `paint` 包含,与 `layout` 包含(见上文)具有相同的附加说明。

另一个优点是,如果应用了包含的元素在屏幕外,浏览器不需要绘制其子元素——这些元素也位于屏幕外,因为它们完全包含在该框内。

大小包含

css
article {
  contain: size;
}

大小包含本身并没有提供多少性能优化。但是,大小包含意味着大小包含元素的子元素的大小不会影响元素本身的大小——它的尺寸计算方式就好像它没有子元素一样。

如果在元素上设置 `contain: size`,你需要使用 `contain-intrinsic-size` 或长格式属性 `contain-intrinsic-width``contain-intrinsic-height`(按此顺序)指定元素的大小。如果未设置大小,元素在大多数情况下可能会被设置为零尺寸。

css
article {
  contain: size;
  contain-intrinsic-size: 100vw auto;
}

样式包含

css
article {
  contain: style;
}

尽管有这个名字,但样式包含并不提供像 Shadow DOM`@scope` 那样作用于范围的样式。`style` 值的主要用例是防止 CSS 计数器 在元素中更改,然后可能影响树的其余部分的情况。

使用 `contain: style` 可确保 `counter-increment``counter-set` 属性创建仅作用于该子树的新计数器。

你可以通过包含多个用空格分隔的值来包含多个包含类型,例如 `contain: layout paint`,或者使用两种 特殊值 中的一种。

特殊值

`contain` 有两个特殊值,它们是前三个或所有四个包含类型的简写。

  • content
  • 严格

我们在上面的示例中遇到了第一个。使用 `contain: content` 将打开 `layout`、`paint` 和 `style` 包含。由于它省略了 `size`,因此它是一个安全的值,可以在广泛应用。

`contain: strict` 声明的行为与声明 `contain: size layout paint style`(包含四个用空格分隔的值)相同,提供了最多的包含。使用它存在风险,因为它应用了 `size` 包含;由于依赖其子元素的大小,框可能会最终变为零尺寸。

为了消除这种风险,在使用 `strict` 时始终设置大小。

css
article {
  contain: strict;
  contain-intrinsic-size: 80vw auto;
}

以上与以下相同

css
article {
  contain: size layout paint style;
  contain-intrinsic-size: 80vw auto;
}

content-visibility

当你有大量的内容需要使用强包含(这些内容通常在屏幕外)——例如,如果所有博客文章在博客主页上都以无限滚动博客的形式可见——可以使用 `content-visibility: auto` 一次性应用所有包含。

`content-visibility` 属性控制元素是否渲染其内容,以及强制实施一组强大的包含,允许用户代理潜在地省略大量布局和渲染工作,直到它变得必要。它使用户代理能够跳过元素的渲染工作(包括布局和绘制),直到它变得必要——这使得初始页面加载速度更快。

它可能的值是

  • visible:默认行为——元素的内容像往常一样布局和渲染。
  • hidden:元素 跳过其内容。跳过的内容将不可供用户代理功能(如页面内查找、标签顺序导航等)使用,也不能被选中或获得焦点。
  • auto:元素打开布局包含、样式包含和绘制包含,就像设置了 `contain: content` 一样。如果元素 与用户无关,它也会跳过其内容。与 `hidden` 不同,跳过的内容仍然可供用户交互,保持可聚焦、可选,处于正常的标签顺序,并且可供内容内搜索使用。

与用户相关

用户代理有一个内容 与用户相关 的概念。如果以下任何一项为真,则元素将变为“与用户相关”。

  • 元素出现在视窗中,或用户代理定义的视窗周围的边距(视窗尺寸的 50%,以便应用程序有时间为元素可见性更改做准备)。
  • 元素或其内容获得焦点。
  • 元素或其内容被选中,例如通过鼠标光标拖动文本或通过其他突出显示操作。
  • 元素或其内容放置在 顶层

当设置了 `content-visibility: auto` 并且浏览器确定内容与用户相关时,浏览器将渲染该内容。

跳过其内容

当你在元素上设置 `content-visibility: hidden` 时,你是在告诉浏览器它与用户无关,因此其 内容应该被跳过 并且不应渲染。这有助于提高性能。

当在元素上设置 `content-visibility: auto` 并且浏览器确定其内容 *不* 与用户相关时,浏览器也会跳过元素的内容。

当元素跳过其内容时

  • 它打开了布局、样式、绘制和大小包含。
  • 它的内容不会被绘制,就好像在它上面设置了 `visibility: hidden` 一样。
  • 它的内容不会接收指针事件,就好像在它上面设置了 `pointer-events: none` 一样。

这在上面提到的两种情况下都会发生,但是对于 `content-visibility: auto`,内容可以被搜索、获得焦点,并且可以从不相关变为相关。这对于 `content-visibility: hidden` 来说不是这种情况。

注意: 要对从 `content-visibility: hidden` 到可见值的转换进行动画处理,你需要设置 `transition-behavior: allow-discrete``@starting-style` 样式。请参阅 转换 `display` 和 `content-visibility`,以了解更多信息。

另请参阅