使用 CSS 锚点定位

**CSS 锚点定位**模块定义了允许您将元素相互关联的功能。元素可以定义为**锚点元素**和**锚点定位元素**。锚点定位元素可以绑定到锚点元素。然后,可以根据其绑定的锚点元素的大小和位置来设置锚点定位元素的大小和位置。

CSS 锚点定位还提供仅使用 CSS 的机制来为锚点定位元素指定多个备用位置。例如,如果工具提示锚定到表单字段,但工具提示在其默认位置设置中会被渲染到屏幕外,则浏览器可以尝试在不同的建议位置渲染它,以便将其放置在屏幕上,或者,如果需要,完全隐藏它。

本文介绍了基本的锚点定位概念,以及如何在基本层面上使用模块的关联、定位和大小调整功能。我们包含了指向参考页面的链接,其中包含每个概念的更多示例和语法详细信息。有关指定备用位置和隐藏锚点定位元素的信息,请参阅处理溢出:尝试回退和条件隐藏

基本概念

通常,您希望将一个元素系链或绑定到另一个元素。例如

  • 与表单控件一起显示的错误消息。
  • 出现在 UI 元素旁边的工具提示或信息框,以提供有关该元素的更多信息。
  • 可以访问以快速配置 UI 元素的设置或选项对话框。
  • 出现在关联的导航栏或按钮旁边的下拉菜单或弹出菜单。

现代界面经常要求某些内容(通常是可重用且动态生成的)相对于锚点元素进行定位。如果要系链的元素(即**锚点元素**)始终位于 UI 中的相同位置,并且系链的元素(即**锚点定位元素**或简称**定位元素**)始终可以在源顺序中紧接其之前或之后,则创建此类用例将非常简单。但是,情况很少如此简单。

定位元素相对于其锚点元素的位置需要保持并根据锚点元素的移动或其他配置更改(例如,通过滚动、更改视口大小、拖放等)进行调整。例如,如果表单字段等元素靠近视口边缘,则其工具提示可能会最终显示在屏幕外。通常,您希望将工具提示绑定到其表单控件并确保工具提示在表单字段可见时保持完全可见在屏幕上,并在需要时自动移动工具提示。您可能已经注意到,当您在桌面或笔记本电脑上右键单击(Ctrl + 单击)桌面上下文菜单时,这是您的操作系统的默认行为。

从历史上看,将一个元素与另一个元素关联,并根据锚点的位置动态更改定位元素的位置和大小需要使用 JavaScript,这增加了复杂性和性能问题。它也不能保证在所有情况下都能正常工作。在CSS 锚点定位模块中定义的功能使您可以使用 CSS(和 HTML)而不是 JavaScript 以高性能且声明的方式实现此类用例。

关联锚点和定位元素

要将元素与锚点关联,您需要首先声明哪个元素是锚点,然后指定要与该锚点关联的哪个定位元素。这可以通过 CSS 或 HTML 的 anchor 属性来完成。

仅 CSS 方法

要使用 CSS 将元素声明为锚点,您需要使用 anchor-name 属性在其上设置锚点名称。锚点名称需要是 <dashed-ident>。在此示例中,我们还将锚点的 width 设置为 fit-content 以获得一个小正方形锚点,这可以更好地演示锚定效果。

css
.anchor {
  anchor-name: --myAnchor;
  width: fit-content;
}

将元素转换为锚点定位元素需要两个步骤:它需要使用 position 属性绝对或固定 定位。然后,定位元素将其 position-anchor 属性设置为锚元素的 anchor-name 属性的值,以将两者关联起来。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
}

我们将以上 CSS 应用于以下 HTML

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

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

这将呈现如下

锚点和信息框现在已关联,但目前您必须相信我们。它们还没有相互绑定——如果您要定位锚点并将其移动到页面上的其他位置,它会自行移动,而信息框会留在相同的位置。当我们查看 基于锚点位置定位元素 时,您将看到实际的绑定效果。

HTML 方法

要将定位元素与 HTML 中的锚点关联,您可以使用 anchor 属性。您需要为锚点元素指定一个 id。然后在锚点定位元素上设置 anchor 属性,其值为要与其关联的锚点元素的 id

我们在下面的 HTML 中执行了此操作

html
<div class="anchor" id="example-anchor">⚓︎</div>

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

元素需要绝对或固定定位才能与锚点关联,因此我们为信息框提供了 positionfixed

css
.infobox {
  position: fixed;
}

这使我们获得了之前使用 CSS 达到的相同结果。我们使用定位元素上的 anchor 属性而不是锚元素的 anchor-name 属性和定位元素的 position-anchor 属性,将定位元素与锚元素关联。

注意:anchor 属性目前的支持度低于 CSS 等效项。有关更多信息,请参阅 anchor 属性兼容性表

我们已经关联了这两个元素,但它们还没有绑定。要将它们绑定在一起,需要相对于其锚点定位定位元素,这可以通过 CSS 完成。

相对于其锚点定位元素

如上所述,将定位元素与锚点关联本身并没有多大用处。我们的目标是将定位元素相对于其关联的锚元素放置。这可以通过在 内边距属性 上设置 CSS anchor() 函数 值、指定 position-area 或使用 anchor-center 布局值 将定位元素居中来完成。

注意:锚元素必须是可见的 DOM 节点才能使关联和定位生效。如果它被隐藏(例如通过 display: none),则定位元素将相对于其最近的定位祖先进行定位。我们在 使用 position-visibility 进行条件隐藏 中讨论了当锚点消失时如何隐藏锚点定位元素。

使用带有 anchor() 函数值的内边距属性

传统的绝对和固定定位元素通过在 内边距属性 上设置 <length><percentage> 值来显式定位。使用 position: absolute,此内边距位置值是相对于最近的定位祖先的边缘的绝对距离。使用 position: fixed,内边距位置值是相对于视口的绝对距离。

CSS 锚点定位改变了这种范式,使锚点定位元素能够相对于其关联的锚点放置。该模块定义了 anchor() 函数,它是每个内边距属性的有效值。使用时,该函数通过定义锚元素、定位元素相对于其进行定位的锚元素的侧面以及距该侧面的距离,将内边距位置值设置为相对于锚元素的绝对距离。

函数组件如下所示

anchor(<anchor-element> <anchor-side>, <fallback>)
<anchor-element>

您要将元素的侧面相对于其进行定位的锚元素的 anchor-name 属性值。这是一个 <dashed-ident> 值。如果省略,则使用元素的**默认锚点**。这是其 position-anchor 属性中引用的锚点,或通过 anchor HTML 属性与元素关联的锚点。

注意:指定 <anchor-element> 会将元素相对于该锚点定位,但不会提供元素关联。只有 position-anchor 属性和 anchor 属性创建关联。虽然可以通过在同一元素的不同 anchor() 函数内指定 不同的 <anchor-element> 来将元素的侧面相对于多个锚点定位,但定位元素仅与单个锚点关联。

<anchor-side>

指定相对于锚点的一侧或多侧的位置。有效值包括锚点的 center、锚点的物理(topleft 等)或逻辑(startself-end 等)侧面,或锚点侧面之间介于 0%start)和 100%end)之间的 <percentage>。如果使用的值与设置 anchor() 函数的内边距属性不兼容,则使用回退值。

<fallback>

一个 <length-percentage>,用于定义如果元素未绝对或固定定位、如果使用的 <anchor-side> 值与设置 anchor() 函数的内边距属性不兼容或如果锚元素不存在时要使用的距离。

anchor() 函数的返回值是根据锚点位置计算的长度值。如果在锚点定位元素的内边距属性上直接设置长度或百分比,则其定位方式就像未绑定到锚元素一样。这与 <anchor-side> 值与设置它的内边距属性不兼容并使用回退值时看到的行为相同。这两个声明是等效的

css
bottom: anchor(right, 50px);
bottom: 50px;

两者都将定位元素放置在元素最近的定位祖先(如果有)或初始包含块的底部上方 50px 处。

您将使用的最常见的 anchor() 参数将引用默认锚点的一侧。您还经常会添加 margin 以在锚点的边缘和定位元素之间创建间距,或者在 calc() 函数中使用 anchor() 以添加该间距。

例如,此规则将定位元素的右侧边缘与锚元素的左侧边缘对齐,然后添加一些 margin-left 以在边缘之间留出一些空间

css
.positionedElement {
  right: anchor(left);
  margin-left: 10px;
}

anchor() 函数的返回值是长度。这意味着您可以在 calc() 函数中使用它。此规则将定位元素的逻辑块结束边缘放置在锚元素的逻辑块开始边缘 10px 处,使用 calc() 函数添加间距,因此我们不需要添加边距

css
.positionedElement {
  inset-block-end: calc(anchor(start) + 10px);
}

anchor() 示例

让我们看一个 anchor() 实际应用的示例。我们使用了与前面示例中相同的 HTML,但在其下方和上方放置了一些填充文本,以导致内容溢出其容器并滚动。我们还将为锚元素提供与前面示例中相同的 anchor-name

css
.anchor {
  anchor-name: --myAnchor;
}

信息框通过锚点名称与锚点关联,并进行固定定位。通过包含 inset-block-startinset-inline-start 属性(在水平从左到右的书写模式下等效于 topleft),我们已将其绑定到锚点。我们向信息框添加 margin 以在定位元素与其锚点之间添加空间

css
.infobox {
  position-anchor: --myAnchor;
  position: fixed;
  inset-block-start: anchor(end);
  inset-inline-start: anchor(self-end);
  margin: 5px 0 0 5px;
}

让我们更详细地了解内边距属性定位声明

  • inset-block-start: anchor(end):这将定位元素的块开始边缘设置为锚点的块结束边缘,使用 anchor(end) 函数计算。
  • inset-inline-start: anchor(self-end):这将定位元素的内联开始边缘设置为锚点的内联结束边缘,使用 anchor(self-end) 函数计算。

这将给我们以下结果

定位元素位于锚元素下方 5px 且右侧 5px 处。如果向上或向下滚动文档,定位元素会保持相对于锚元素的位置——它固定到锚元素,而不是视口。

设置 position-area

position-area 属性为相对于锚点定位元素提供了一种替代 anchor() 函数的方法。position-area 属性基于 3x3 网格平铺的概念,其中锚元素是中心平铺。position-area 属性可用于将锚点定位元素放置在九个平铺中的任何一个,或使其跨越两个或三个平铺。

The position-area grid, as described below

网格平铺分为行和列

  • 三行由物理值 topcenterbottom 表示。它们还具有逻辑等效项,如 startcenterend,以及坐标等效项,如 y-startcentery-end
  • 三列由物理值 leftcenterright 表示。它们还具有逻辑等效项,如 startcenterend,以及坐标等效项,如 x-startcenterx-end

中心平铺的尺寸由锚元素的 包含块 定义,而中心平铺与网格外边缘之间的距离由定位元素的包含块定义。

position-area 属性值由一个或两个值组成,基于上面描述的行和列值,并提供跨越选项以定义应在其中定位元素的网格区域。

例如

您可以指定两个值,将定位元素放置在特定的网格单元格中。例如

  • top left(逻辑等效于start start)会将定位元素放置在左上角的单元格中。
  • bottom center(逻辑等效于end center)会将定位元素放置在底部中心的单元格中。

您可以指定行或列值加上一个span-*值。第一个值指定放置定位元素的行或列,最初将其放置在中心,第二个值指定该列的跨度。例如

  • top span-left导致定位元素放置在顶行,并跨越该行的中心和左侧单元格。
  • y-end span-x-end导致定位元素放置在y列的末端,并跨越该列的中心和x末端单元格。
  • block-end span-all导致定位元素放置在块末端行,并跨越该行的内联开始、中心和内联结束单元格。

如果您只指定一个值,则效果会根据设置的值而有所不同

  • 物理侧值(topbottomleftright)或坐标值(y-starty-endx-startx-end)的作用就像另一个值为span-all一样。例如,toptop span-all的效果相同。
  • 逻辑侧值(startend)的作用就像另一个值设置为相同的值一样;例如,startstart start的效果相同。
  • 值为center的作用就像两个值都设置为center一样(因此,center center)。

注意:有关所有可用值的详细说明,请参阅<position-area>值参考页面。混合逻辑值和物理值将使声明无效。

让我们演示其中一些值;此示例使用与上一个示例相同的HTML和基本CSS样式,除了我们包含了一个<select>元素以启用更改定位元素的position-area值。

信息框被赋予固定定位,并使用CSS与锚点关联。加载时,它被设置为使用position-area: top;绑定到锚点,这会导致它位于位置区域网格的顶部。一旦您从<select>菜单中选择不同的值,这将被覆盖。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: top;
}

我们还包含一个简短的脚本,将从<select>菜单中选择的新的position-area值应用于信息框

js
const infobox = document.querySelector(".infobox");
const selectElem = document.querySelector("select");

selectElem.addEventListener("change", () => {
  const area = selectElem.value;

  // Set the position-area to the value chosen in the select box
  infobox.style.positionArea = area;
});

尝试从<select>菜单中选择新的position-area值,以查看它们对信息框位置的影响

定位元素宽度

在上面的示例中,我们没有在任何维度上明确设置定位元素的大小。我们故意省略了大小调整,以便您可以观察由此产生的行为。

当定位元素被放置到position-area网格单元格中且没有显式大小调整时,它会与指定的网格区域对齐,并表现得好像width被设置为max-content一样。它的大小根据其包含块大小确定,即其内容的宽度。此大小是通过设置position: fixed强加的。自动大小调整的绝对和固定定位元素会自动调整大小,根据需要扩展以适应文本内容,同时受视口边缘的约束。在这种情况下,当放置在网格的左侧并使用任何leftinline-start值时,文本会换行。如果锚定元素的max-content大小比其锚点窄或短,则它们不会增长以匹配锚点的大小。

如果定位元素垂直居中,例如使用position-area: bottom center,它将与指定的网格单元格对齐,并且宽度将与锚元素相同。在这种情况下,其最小高度是锚元素的包含块大小。它不会溢出,因为min-widthmin-content,这意味着它将至少与其最长的单词一样宽。

使用 anchor-center 在锚点上居中

虽然您可以使用position-areacenter值使锚定位元素居中,但内边距属性与anchor()函数结合使用可以更好地控制精确位置。CSS锚定位提供了一种方法,可以在使用内边距属性而不是position-area进行绑定时,使锚定位元素相对于其锚点居中。

属性justify-selfalign-selfjustify-itemsalign-items(及其place-itemsplace-self简写)旨在允许开发人员轻松地在各种布局系统中沿内联或块方向对齐元素,例如在弹性子元素的情况下沿主轴或交叉轴对齐。CSS锚定位为这些属性提供了一个额外的值anchor-center,该值使定位元素与其默认锚点的中心对齐。

此示例使用与上一个示例相同的HTML和基本CSS。信息框被赋予固定定位,并绑定到锚点的底部边缘。然后使用justify-self: anchor-center确保它在锚点的水平中心。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  top: calc(anchor(bottom) + 5px);
  justify-self: anchor-center;
}

这使锚定位元素在其锚点的底部居中。

根据锚点大小调整元素大小

除了相对于其锚点的位置定位元素之外,您还可以使用大小调整属性值中的anchor-size()函数相对于其锚点的大小调整元素。

可以接受anchor-size()值的尺寸调整属性包括

anchor-size()函数解析为<length>值。它们的语法如下所示

anchor-size(<anchor-element> <anchor-size>, <length-percentage>)
<anchor-element>

作为您要相对于其调整元素大小的锚元素的anchor-name属性值的<dashed-ident>名称集。如果省略,则使用元素的**默认锚点**,即position-anchor属性中引用的锚点。

<anchor-size>

指定定位元素将相对于其调整大小的锚元素的尺寸。这可以使用物理(widthheight)或逻辑(inlineblockself-inlineself-block)值来表示。

<length-percentage>

指定用作后备值的大小,如果元素不是绝对或固定定位,或者锚元素不存在。

您将使用的最常见的anchor-size()函数只会引用默认锚点的尺寸。您还可以将它们用于calc()函数中,以修改应用于定位元素的大小。

例如,此规则将定位元素的宽度设置为等于默认锚元素的宽度

css
.elem {
  width: anchor-size(width);
}

此规则将定位元素的内联大小设置为锚元素的内联大小的4倍,乘法在calc()函数内完成。

css
.elem {
  inline-size: calc(anchor-size(self-inline) * 4);
}

让我们看一个例子。HTML和基本CSS与前面的示例相同,除了锚元素被赋予一个tabindex="0"属性以使其可聚焦。信息框被赋予固定定位,并与锚点关联,方式与之前相同。但是,这次我们使用position-area将其绑定到锚点的右侧,并将其宽度设置为锚点宽度的5倍。

css
.infobox {
  position: fixed;
  position-anchor: --myAnchor;
  position-area: right;
  margin-left: 5px;
  width: calc(anchor-size(width) * 5);
}

此外,我们增加了锚元素的width:hover:focus上,并赋予它一个transition,以便在状态更改时进行动画。

css
.anchor {
  text-align: center;
  width: 30px;
  transition: 1s width;
}

.anchor:hover,
.anchor:focus {
  width: 50px;
}

将鼠标悬停在锚元素上或使用Tab键切换到锚元素 - 定位元素随着锚元素的增长而增长,这表明锚定位元素的大小相对于其锚点。

另请参阅