处理溢出:尝试回退和条件隐藏

当使用 CSS 锚点定位 时,一个重要的考虑因素是确保锚点定位的元素始终出现在用户方便交互的位置(如果可能),无论锚点位于何处。例如,当您滚动页面时,锚点及其关联的定位元素将向视口边缘移动。当定位元素开始溢出视口时,您需要更改其位置以将其重新放回屏幕上,例如在锚点的另一侧。

或者,在某些情况下,可能更倾向于隐藏溢出的定位元素——例如,如果其锚点位于屏幕外,则其内容可能没有意义。

本指南说明了如何使用 CSS 锚点定位机制来管理这些问题——**position-try 回退选项**和**条件隐藏**。Position-try 回退选项为浏览器提供了备选位置,以便在定位元素开始溢出时尝试放置,以使其保持在屏幕上。条件隐藏允许指定锚点或定位元素将在其下隐藏的条件。

**注意:**有关 CSS 锚点定位基本原理的信息,请参阅 使用 CSS 锚点定位

功能摘要

如果工具提示固定在 UI 元素的右上角,当用户滚动内容使 UI 功能位于视口的右上角时,该 UI 功能的工具提示将已滚动出屏幕。CSS 锚点定位解决了此类问题。模块的 position-try-fallbacks 属性指定浏览器尝试防止定位元素溢出的一个或多个备选 position-try 回退选项。

可以使用以下方法指定 Position-try 回退选项

此外,position-try-order 属性允许您指定各种选项,这些选项会导致优先于元素的初始定位设置可用的 position try 选项。例如,您可能希望最初在具有更多可用高度或宽度的空间中显示元素。

可以使用简写属性 position-try 在单个声明中指定 position-try-orderposition-try-fallbacks 值。

在某些情况下,如果锚点位于屏幕外,则锚点定位的内容没有意义,反之亦然。例如,您可能有一个包含测验问题的锚点,以及包含在关联定位元素中的答案,并希望将它们一起显示或根本不显示。这可以通过条件隐藏来实现,该条件隐藏通过 position-visibility 属性进行管理。此属性采用各种值来定义溢出元素将被隐藏的条件。

预定义的回退选项

position-try-fallbacks 属性的预定义回退选项值(在规范中定义为 <try-tactic>)如果元素否则会溢出,则会“翻转”锚点定位元素在一个或两个轴上的位置。

元素可以设置为沿块轴翻转(flip-block)、沿内联轴翻转(flip-inline),或沿从锚点的一个角穿过其中心到其对角线的假想线对角翻转(flip-start)。这三个值都会翻转元素,将其位置镜像到相反的一侧(前两个值),以及相邻的一侧(flip-start)。例如,如果一个位于其锚点上方 10px 的元素开始溢出到锚点的顶部,则 flip-block 值会将该定位元素翻转到其锚点下方 10px 的位置。

在这个示例中,我们包含了两个 <div> 元素。第一个将是我们的锚点元素,第二个将相对于锚点进行定位。

html
<div class="anchor">⚓︎</div>

<div class="infobox">
  <p>This is an information box.</p>
</div>

我们将 <body> 元素的样式设置为比视口更大,以便我们可以在视口中水平和垂直滚动锚点和定位元素。

css
body {
  width: 1500px;
  height: 500px;
}

为了说明目的,我们将锚点绝对定位,使其出现在初始 <body> 渲染的中心附近。

锚点定位元素被赋予固定定位,并使用 position-area 与锚点的左上角绑定。它被赋予 position-try-fallbacks: flip-block, flip-inline; 以提供一些备用选项来移动定位元素,以防止它在锚点靠近视口边缘时溢出。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: top left;
  position-try-fallbacks: flip-block, flip-inline;
}

注意:当指定多个位置尝试回退选项时,它们之间用逗号分隔,并按指定的顺序尝试。

尝试滚动演示,使锚点开始靠近边缘。

  • 将锚点移动到视口的顶部。定位元素翻转到锚点的左下方以避免溢出。
  • 将锚点移动到视口的左侧。定位元素翻转到锚点的右上方以避免溢出。

如果您将锚点移动到视口的左上角,您会注意到一个问题——当定位元素开始在块和内联方向溢出时,它会翻回到其默认的左上角位置并在两个方向上溢出,这不是我们想要的。

发生这种情况是因为我们只向浏览器提供了 flip-block flip-inline 的位置选项。我们没有给它同时尝试这两个选项的选项。浏览器尝试回退选项,寻找一个导致定位元素完全渲染在视口或包含块内的选项。如果找不到,它会将定位元素渲染在其最初定义的渲染位置,不应用任何位置回退选项。

下一节演示如何解决此问题。

将多个值组合成一个选项

可以在逗号分隔的 position-try-fallbacks 列表中,将多个 预定义尝试回退选项自定义尝试选项 名称放入单个空格分隔的尝试回退选项值中。在尝试应用这些回退选项时,浏览器会将各个效果组合成一个组合的回退选项。

让我们使用一个组合的尝试回退选项来修复我们在上一个演示中发现的问题。此演示中的 HTML 和 CSS 与上一个演示相同,除了信息框定位样式。在这种情况下,它被赋予了第三个位置尝试回退选项:flip-block flip-inline

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: top left;
  position-try-fallbacks:
    flip-block,
    flip-inline,
    flip-block flip-inline;
}

这意味着浏览器将首先尝试 flip-block,然后尝试 flip-inline 以避免溢出。如果这两个回退选项都失败,它将尝试将两者结合起来,同时在块内联方向翻转元素的位置。现在,当您将锚点滚动到视口的顶部左侧边缘时,定位元素将翻转到右下方。

使用 position-area 尝试回退选项

预定义的 <try-tactic> 尝试回退选项很有用但有限,因为它们仅允许定位元素放置在轴上翻转。如果您有一个定位在其锚点左上方的锚点定位元素,并且希望在它开始溢出时将其位置更改为锚点的正下方,该怎么办?

为了实现这一点,您可以使用 position-area 值作为位置尝试回退选项,将其包含在 position-try-fallbacks 列表中。这会根据该位置区域自动创建一个尝试回退选项。实际上,它是创建仅包含该 position-area 属性值的 自定义位置选项 的快捷方式。

以下示例显示了 position-area 位置尝试回退选项的使用。我们使用相同的 HTML 和 CSS,除了信息框定位。在这种情况下,我们的位置尝试回退选项是 position-area 值——toptop-rightrightbottom-rightbottombottom-leftleft。无论锚点接近视口的哪个边缘,定位元素都将被合理地定位。这种详细的方法比预定义值方法更细粒度且更灵活。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: top left;
  position-try-fallbacks:
    top, top right, right,
    bottom right, bottom,
    bottom left, left;
}

注意:您不能将 position-area 尝试回退选项添加到位置尝试回退列表中的空格分隔组合位置选项中。

滚动页面并查看这些位置尝试回退选项在锚点接近视口边缘时的效果。

自定义回退选项

要使用通过上述机制不可用的自定义位置回退选项,您可以使用 @position-try at-rule 创建自己的选项。语法如下:

@position-try --try-fallback-name {
  descriptor-list
}

--try-fallback-name 是开发人员为位置尝试回退选项定义的名称。然后可以在 position-try-fallbacks 属性值中逗号分隔的尝试回退选项列表中指定此名称。如果多个 @position-try 规则具有相同的名称,则文档顺序中的最后一个规则会覆盖其他规则。避免为您的尝试回退选项您的锚点或自定义属性名称使用相同的名称;它不会使 at-rule 无效,但会使您的 CSS 难以理解。

descriptor-list 定义该单个自定义尝试回退选项的属性值,包括如何放置和调整定位元素的大小,以及任何边距。允许的属性描述符的有限列表包括

如果您包含在 at-rule 中的值,则如果应用了命名的自定义尝试回退选项,这些值将应用于定位元素。如果之前在定位元素上设置了任何属性,则这些属性值将被描述符值覆盖。如果用户滚动,导致应用不同的尝试回退选项或不应用任何尝试回退选项,则先前应用的尝试回退选项中的值将被取消设置。

在此示例中,我们设置并使用了几个自定义尝试回退选项。我们使用与前面示例相同的 HTML 和 CSS 代码。

我们首先使用 @position-try 定义四个自定义尝试回退选项。

css
@position-try --custom-left {
  position-area: left;
  width: 100px;
  margin: 0 10px 0 0;
}

@position-try --custom-bottom {
  position-area: bottom;
  margin: 10px 0 0 0;
}

@position-try --custom-right {
  position-area: right;
  width: 100px;
  margin: 0 0 0 10px;
}

@position-try --custom-bottom-right {
  position-area: bottom right;
  margin: 10px 0 0 10px;
}

创建自定义尝试回退选项后,我们可以通过引用其名称将它们包含在位置列表中。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: top;
  width: 200px;
  margin: 0 0 10px 0;
  position-try-fallbacks:
    --custom-left, --custom-bottom,
    --custom-right, --custom-bottom-right;
}

请注意,我们的默认位置由 position-area: top 定义。当信息框在任何方向上都没有溢出页面时,信息框位于锚点上方,并且 position-try-fallbacks 属性中设置的位置尝试回退选项将被忽略。此外,请注意信息框具有设置的固定宽度和底部边距。这些值将随着应用不同的位置尝试回退选项而改变。

如果信息框开始溢出,浏览器将尝试 position-try-fallbacks 属性中列出的位置选项。

  • 浏览器首先尝试 --custom-left 回退位置。这会将信息框移动到锚点的左侧,调整边距以适应,并为信息框提供不同的宽度。
  • 接下来,浏览器尝试 --custom-bottom 位置。这会将信息框移动到锚点的底部,并设置适当的边距。它不包含 width 描述符,因此信息框恢复到由 width 属性设置的默认宽度 200px
  • 浏览器接下来尝试 --custom-right 位置。这与 --custom-left 位置的工作方式大致相同,应用了相同的 width 描述符值,但 position-areamargin 值被镜像以将信息框适当地放置在右侧。
  • 如果其他任何回退都无法阻止定位元素溢出,则浏览器会尝试 --custom-bottom-right 位置作为最后手段。这与其他回退选项的工作方式大致相同,但它会将定位元素放置在锚点的右下方。

如果所有回退都无法阻止定位元素溢出,则位置将恢复到初始 position-area: top; 值。

注意:当应用位置尝试回退选项时,其值将覆盖在定位元素上设置的默认值。例如,在定位元素上设置的默认 width200px,但当应用 --custom-right 位置尝试回退选项时,其宽度将设置为 100px

滚动页面并查看这些位置尝试回退选项在锚点接近视口边缘时的效果。

使用 position-try-order

position-try-order 属性与其余位置尝试功能略有不同,因为它在首次显示定位元素时使用位置尝试回退选项,而不是在它正在溢出的过程中。

此属性允许您指定希望使用提供其包含块最大宽度或最大高度的位置尝试回退选项来初始显示定位元素。这是通过设置 most-heightmost-widthmost-block-sizemost-inline-size 值来实现的。您还可以使用 normal 值删除任何先前设置的 position-try-order 值的效果。

如果没有任何位置尝试回退选项可以提供比分配给元素的初始定位更大的宽度/高度,则 position-try-order 不会产生任何影响。

让我们看一下一个演示,该演示显示了此属性的效果。HTML 与前面示例中的相同,只是我们添加了一个包含单选按钮的 <form>,允许您选择不同的 position-try-order 值以查看其效果。

我们包含一个自定义尝试回退选项——--custom-bottom——它将元素定位在锚点下方并添加边距。

css
@position-try --custom-bottom {
  top: anchor(bottom);
  bottom: unset;
  margin-top: 10px;
}

我们最初将信息框定位在锚点的顶部,然后赋予它我们的自定义尝试回退。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  bottom: anchor(top);
  margin-bottom: 10px;
  justify-self: anchor-center;
  position-try-fallbacks: --custom-bottom;
}

最后,我们包含一些 JavaScript 代码,这些代码在单选按钮上设置了一个 change 事件处理程序。当选择一个单选按钮时,其值将应用于信息框的 position-try-order 属性。

js
const infobox = document.querySelector(".infobox");
const radios = document.querySelectorAll('[name="position-try-order"]');

for (const radio of radios) {
  radio.addEventListener("change", setTryOrder);
}

function setTryOrder(e) {
  const tryOrder = e.target.value;
  infobox.style.positionTryOrder = tryOrder;
}

尝试选择 most-height 顺序选项。这会应用 --custom-bottom 位置尝试回退选项的效果,该选项将元素定位在锚点下方。发生这种情况是因为锚点下方比锚点上方有更多的空间。

有条件地隐藏锚点定位的元素

在某些情况下,您可能希望隐藏锚点定位元素。例如,如果锚点元素被裁剪,因为它太靠近视口边缘,您可能只想完全隐藏其关联的元素。position-visibility 属性允许您指定隐藏定位元素的条件。

默认情况下,定位元素始终显示。no-overflow 值会在强制隐藏定位元素,如果它开始溢出其包含元素或视口。

另一方面,anchors-visible 值会在其关联锚点完全隐藏时强制隐藏定位元素,无论是溢出其包含元素(或视口)还是被其他元素覆盖。如果锚点的任何部分仍然可见,则定位元素将可见。

强制隐藏的元素的行为就像其自身及其后代元素都设置了visibility值为hidden一样,无论其实际的visibility值是什么。

让我们看看此属性的实际应用。

此示例使用与前面示例相同的 HTML 和 CSS,其中信息框与锚点的底部边缘绑定。信息框被赋予position-visibility: no-overflow;,以便在向上滚动到其开始溢出视口的位置时将其完全隐藏。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  margin-bottom: 5px;
  position-area: top span-all;
  position-visibility: no-overflow;
}

向下滚动页面,并注意当定位元素到达视口顶部时它是如何隐藏的。

另请参阅