使用容器滚动状态查询

容器滚动状态查询是一种容器查询。它不像基于容器大小选择性地应用样式到后代元素,而是允许你基于容器的滚动状态选择性地应用样式到后代元素。这可以包括容器是否部分滚动、是否吸附到滚动吸附容器祖先,或者是否通过position: sticky定位并粘滞到滚动容器祖先的边界。

本文解释了如何使用容器滚动状态查询,并提供了每种类型的一个示例。它假设你了解容器查询的基础知识。如果你是容器查询的新手,请在继续之前阅读CSS 容器查询

容器滚动状态查询的类型

scroll-state() 查询中,你可以使用以下三个 @container 描述符:

  • scrollable:查询容器是否可以通过用户启动的滚动(例如通过拖动滚动条或使用触控板手势)在给定方向上滚动。换句话说,给定方向上是否有任何溢出内容可以滚动到?这对于应用与滚动容器的滚动位置相关的样式非常有用。例如,你可以在滚动条位于顶部时显示一个提示,鼓励人们向下滚动查看更多内容,并在用户实际开始滚动时将其隐藏。
  • snapped:查询容器是否将沿给定轴吸附到滚动吸附容器祖先。这对于在元素吸附到滚动吸附容器时应用样式非常有用。例如,你可能希望以某种方式突出显示吸附的元素,或者显示其以前隐藏的某些内容。
  • stuck:查询具有 sticky position 值的容器是否粘滞到其滚动容器祖先的边缘。这对于在 position: sticky 元素粘滞时对其进行不同的样式设置非常有用——例如,你可以为它们提供不同的配色方案或布局。

语法概述

要将容器元素建立为滚动状态查询容器,请在其上设置 container-type 属性,并将其值设置为 scroll-state。你也可以选择为其指定一个 container-name,以便可以使用特定的容器查询来定位它。

css
.container {
  container-type: scroll-state;
  container-name: my-container;
}

然后,你可以创建一个 @container 块,该块指定查询、如果测试通过则应用于容器子元素的规则,以及可选的要查询的容器的 container-name。如果你不指定 container-name,则容器查询将应用于页面上的所有滚动状态查询容器。

在这里,我们只查询名为 my-container 的容器,以确定容器是否可以向其顶部边缘滚动。

css
@container my-container scroll-state(scrollable: top) {
  /* CSS rules go here */
}

注意:为了将滚动状态查询与其他容器查询分开,滚动状态描述符和值放置在括号内,前面加上 scroll-statescroll-state( ... ))。这些构造看起来像函数,但它们不是。

使用 scrollable 查询

滚动状态 scrollable 查询,写为 scroll-state(scrollable: value),测试容器的滚动祖先是否可以通过用户启动的滚动在给定方向上滚动。如果不能,查询返回 false。

value 表示你正在测试滚动可用性的方向,例如:

  • top:测试容器是否可以向其顶部边缘滚动。
  • inline-end:测试容器是否可以向其内联结束边缘滚动。
  • y:测试容器是否可以沿其 y 轴的任一或两个方向滚动。

如果测试通过,则 @container 块内的规则将应用于匹配滚动容器的后代。

让我们看一个例子,其中我们有一个充满内容的滚动容器,以及一个方便的小链接,如果需要可以滚动回顶部。我们将使用 scrollable 查询,仅当用户开始向下滚动内容时才显示该链接。

HTML

在 HTML 中,我们有一个 <article> 元素,其中包含足够的内容以使文档滚动,前面是一个返回顶部链接

html
<a class="back-to-top" href="#" aria-label="Top of page">↑</a>
<article>
  <h1>Reader with container query-controlled "back-to-top" link</h1>
  <section>
    <header>
      <h2>This first section is interesting</h2>

      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </header>

    ...
  </section>

  ...
</article>

为了简洁起见,我们隐藏了大部分 HTML。

CSS

.back-to-top 链接被赋予 fixedposition 值,放置在视口的右下角,并使用 80px 0translate 值移出视口。当任一值更改时,transition 值将动画化 translatebackground-color

css
.back-to-top {
  width: 64px;
  height: 64px;
  color: white;
  text-align: center;
  position: fixed;
  bottom: 10px;
  right: 10px;
  translate: 80px 0;
  transition:
    0.4s translate,
    0.2s background-color;
}

此示例中的滚动容器<html> 元素本身,它被指定为滚动状态查询容器,其 container-type 值为 scroll-statecontainer-name 并非严格必需,但在代码添加到具有多个通过不同查询定位的滚动状态查询容器的代码库中时非常有用。

css
html {
  container-type: scroll-state;
  container-name: scroller;
}

接下来,我们定义一个 @container 块,该块设置此查询的目标容器名称,以及查询本身——scrollable: top。此查询仅当 <html> 元素可以向其顶部边缘滚动时才应用块内包含的规则——换句话说,如果容器之前已向下滚动。如果是这种情况,则将 translate: 0 0 应用于 .back-to-top 链接,这将使其平滑地回到屏幕上。

css
@container scroller scroll-state(scrollable: top) {
  .back-to-top {
    translate: 0 0;
  }
}

为了简洁起见,我们隐藏了示例 CSS 的其余部分。

结果

尝试向下滚动文档,并注意“返回顶部”链接如何因此出现,由于 transition 而从视口右侧平滑动画。如果你通过激活链接或手动滚动返回顶部,“返回顶部”链接将平滑地移出屏幕。

使用 snapped 查询

仅当实现滚动吸附时才相关,滚动状态snapped查询(写为scroll-state(snapped: value))测试容器是否将沿给定轴吸附到滚动吸附容器祖先。如果不是,查询返回 false。

在这种情况下,value 表示你正在测试元素吸附能力的方向,例如:

  • x:测试容器是否水平吸附到其滚动吸附容器祖先。
  • inline:测试容器是否在内联方向上吸附到其滚动吸附容器祖先。
  • y:测试容器是否在两个方向上吸附到其滚动吸附容器祖先。

要评估具有非 none snapped 滚动状态查询的容器,它必须是一个具有滚动吸附容器祖先的容器,也就是说,该祖先的 scroll-snap-type 值不是 none。容器查询 scroll-state(snapped: none) 匹配没有滚动容器祖先的滚动状态容器。

评估将在滚动吸附容器上触发 scrollsnapchanging 事件时进行。

如果测试通过,则 @container 块内的规则将应用于匹配的滚动吸附目标容器的后代。

在此示例中,我们将查看一个具有垂直吸附子元素的滚动吸附容器,并使用 snapped 查询仅在子元素吸附或即将吸附时为其设置样式。

HTML

HTML 由一个 <main> 元素组成,该元素将成为滚动吸附容器。内部有几个 <section> 元素,它们将成为吸附目标。每个 <section> 都包含一个包装器 <div> 和一个 <h2> 标题。包含包装器的目的是创建一个样式目标,因为容器查询允许对容器的后代进行样式设置,而不是容器本身。

html
<main>
  <section>
    <div class="wrapper">
      <h2>Section 1</h2>
    </div>
  </section>

  ...
</main>

为了简洁起见,我们隐藏了大部分 HTML。

CSS

我们在 <main> 元素上设置了 scrolloverflow 值和固定的 height,以将其转换为垂直滚动容器。我们还设置了 y mandatoryscroll-snap-type 值,将 <main> 转换为滚动吸附容器,吸附目标将沿 y 轴吸附到该容器;mandatory 意味着吸附目标将始终被吸附到。

css
main {
  overflow: scroll;
  scroll-snap-type: y mandatory;
  height: 450px;
  width: 250px;
  border: 3px solid black;
}

通过设置非 nonescroll-snap-align 值,将 <section> 元素指定为吸附目标。center 值表示它们将以其中心点吸附到容器。

css
section {
  font-family: "Helvetica", "Arial", sans-serif;
  width: 150px;
  height: 150px;
  margin: 50px auto;

  scroll-snap-align: center;
}

我们希望能够查询 <section> 元素。具体来说,我们希望测试 <section> 元素是否正在吸附到其容器,因此我们通过在其上设置 scroll-statecontainer-type 值,将它们指定为滚动状态查询容器。我们还为它们指定了一个 container-name,这并非严格必需,但如果我们的代码以后变得更复杂,并且我们有多个希望使用不同查询定位的滚动状态查询容器,则会很有用。

css
section {
  container-type: scroll-state;
  container-name: snap-container;
}

接下来,我们定义一个 @container 块,该块设置我们在此查询中定位的容器名称以及查询本身——snapped: y。此查询仅当 <section> 元素垂直吸附到其容器时才应用块中包含的规则。如果是这种情况,我们将新的 backgroundcolor 应用于 <section> 元素的子 .wrapper <div> 以突出显示它。

css
@container snap-container scroll-state(snapped: y) {
  .wrapper {
    background: purple;
    color: white;
  }
}

结果

渲染结果如下所示。尝试上下滚动容器,并注意当 <section> 吸附到其容器时,其样式如何变化。

使用 stuck 查询

滚动状态stuck查询(写为scroll-state(stuck: value))测试具有stickyposition值的容器是否粘滞到其滚动容器祖先的边缘。如果不是,则查询返回 false。

在这种情况下,value 表示你正在测试的滚动容器边缘,例如:

  • top:测试容器是否粘滞到其滚动容器祖先的顶部边缘。
  • block-end:测试容器是否粘滞到其滚动容器祖先的块结束边缘。
  • none:测试容器是否未粘滞到其滚动容器祖先的任何边缘。请注意,即使容器未设置 position: stickynone 查询也会匹配。

如果查询返回 true,则 @container 块内的规则将应用于匹配的 position: sticky 容器的后代。

让我们看一个例子,其中有一个包含溢出内容的滚动容器,其中的标题设置为 position: sticky,并在滚动到该位置时粘滞到容器的顶部边缘。我们将使用 stuck 滚动状态查询,以便在标题粘滞到顶部边缘时对其进行不同的样式设置。

HTML

在 HTML 中,我们有一个 <article> 元素,其中包含足够的内容以导致文档滚动。它使用几个 <section> 元素进行结构化,每个元素都包含一个带有嵌套内容的 <header>

html
<article>
  <h1>Sticky reader with scroll-state container query</h1>
  <section>
    <header>
      <h2>This first section is interesting</h2>

      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    </header>

    ...
  </section>

  <section>
    <header>
      <h2>This one, not so much</h2>

      <p>Confecta res esset.</p>
    </header>

    ...
  </section>

  ...
</article>

为了简洁起见,我们隐藏了大部分 HTML。

CSS

每个 <header> 都具有 stickyposition 值和 0top 值,这使得它们粘滞到滚动容器的顶部边缘。为了测试 <header> 元素是否粘滞到容器顶部边缘,它们被指定为滚动状态查询容器,其 container-type 值为 scroll-statecontainer-name 并非严格必需,但如果此代码添加到具有多个通过不同查询定位的滚动状态查询容器的代码库中,则会很有用。

css
header {
  background: white;
  position: sticky;
  top: 0;
  container-type: scroll-state;
  container-name: sticky-heading;
}

我们还为 <header> 元素中的 <h2><p> 元素提供了一些基本样式,以及一个 transition 值,以便当它们的 background 值更改时,它们会平滑地动画。

css
h2,
header p {
  margin: 0;
  transition: 0.4s background;
}

h2 {
  padding: 20px 5px;
  margin-bottom: 10px;
}

header p {
  font-style: italic;
  padding: 10px 5px;
}

接下来,我们定义一个 @container 块,该块设置我们在此查询中定位的容器名称以及查询本身——stuck: top。此查询仅当 <header> 元素粘滞到其滚动容器的顶部时才应用块中包含的规则。在这种情况下,不同的 backgroundbox-shadow 将应用于包含的 <h2><p>

css
@container sticky-heading scroll-state(stuck: top) {
  h2,
  p {
    background: #cccccc;
    box-shadow: 0 5px 2px #00000077;
  }
}

为了简洁起见,我们隐藏了 CSS 的其余部分。

结果

尝试上下滚动文档,并注意当 <h2><p> 元素粘滞到其容器顶部边缘时,它们如何转换为新的配色方案。

另见