放置提示框或下拉菜单时,您通常希望将其定位在网页上的另一个元素旁边。虽然之前也有一些方法可以使用绝对定位来实现这种效果,但对于更复杂的要求,过去通常会使用 JavaScript 来定位项目。
CSS 锚点定位提供了一种以声明方式相对于另一个元素定位元素的方法。
网络共享元素
若要将某个元素设为锚点,请为其指定一个以两个短划线开头的任意字符串作为 anchor-name
值。这是定位元素将用于查找其锚点的标识符,最好为其指定一个描述性名称。如果某个元素将以不同的方式用作锚点,您甚至可以为其指定多个锚点名称。
您需要在定位的元素上设置一些属性,以便将其系留。首先,您需要通过设置 position: absolute
或 position: fixed
将元素从文档流中拉出,使其浮动。
接下来,您需要通过将 position-anchor
设置为在锚点上设置的锚点名称,来设置要系绳到的锚点。
最后,您需要设置锚点的位置。在本单元稍后的部分中,您将进一步了解 position-area
。
#anchor {
anchor-name: --my-anchor;
}
#positionedElement {
position: absolute;
position-anchor: --my-anchor;
position-area: end;
}
隐式连接
弹出式窗口的关联方式更加简单。当您使用带有 popovertarget
的按钮打开 popover 或通过 showPopover({source})
设置 source
时,popover 已设置“隐式锚点”。由于默认情况下,弹出式窗口已通过 position: fixed
浮动,因此要定位弹出式窗口,您只需设置位置即可。
#anchor{}
#positionedElement {
position-area: end;
margin: unset;
}
确定潜在的锚定广告
您可以将锚点定位实现为组件的一部分,以便在多个位置使用下拉菜单之类的模式。如果您多次使用同一 anchor-name
,如何确保每个定位元素都能找到正确的锚点?
JavaScript 解决方案涉及为每个锚点添加唯一 ID,然后从定位元素引用该 ID。这会变得很麻烦,而 CSS 通过 anchor-scope
提供了一个更简单的解决方案。
anchor-scope
属性用于设置仅在元素及其后代中匹配哪些锚点名称。它接受一个或多个锚点名称的列表或关键字 all
,以限制所有已定义的锚点名称的范围。
最好将 anchor-scope
添加到定位元素和锚定元素的共同祖先中,该祖先不包含其他同名的锚定元素。通常,此属性位于可重用组件的根目录中。
以下示例展示了 anchor-scope
应用于具有相同 anchor-name
的重复元素时所产生的差异。在此示例中,所有 <img>
元素和图片横幅都引用了 --image
锚点名称。当 anchor-scope
应用于 <li>
元素时,position-anchor: --image
将仅匹配与横幅位于同一 <li>
元素内的 <img>
元素,否则将匹配最后呈现的 <img>
。
定位
现在,您已将元素与锚点相关联,接下来可以定位元素了。锚点定位提供了两种定位方法 - position-area
和 anchor()
函数。
position-area
借助 position-area
属性,您可以通过指定一个或两个关键字来将元素定位在锚点周围。这涵盖了许多常见的使用场景,通常是一个不错的起点。
position-area
的工作方式
position-area
的工作原理是,在由锚点边缘和定位元素的原始包含块生成的区域中,为定位元素创建一个新的包含块。
虽然 position-area
有很多可用的关键字,但我们可以将它们分为几个类别,以便更好地理解。Anchor-tool.com 是一个用于探索语法的出色工具。
实体关键字
您可以使用实体关键字 top
、left
、bottom
、right
和 center
。例如,position-area: top right
会将定位元素放置在锚点的上方和右侧。这些关键字还具有对应的实物轴,即 y-start
、x-start
、y-end
和 x-end
。
逻辑关键字
您还可以使用逻辑关键字 block-start
、block-end
、inline-start
和 inline-end
。例如,在英语等语言中,position-area: block-end inline-start
会将定位元素放置在锚点的下方和左侧;在文档的书写模式中,position-area: block-end inline-start
会将定位元素放置在块轴上的锚点之后和内联轴上的锚点之前。center
还可以与逻辑关键字搭配使用。
如果您指定的是逻辑关键字,也可以省略轴,但要先指定块轴,再指定内联轴。position-area: start end
与 position-area: block-start inline-end
相同,甚至与 position-area: inline-end block-start
相同。
跨多个网格区域
到目前为止,您可能已经注意到,这些选项仅允许您将定位元素放置在单个网格空间内。向物理或逻辑属性添加 span
前缀会添加相邻的中心网格空间。position-area: span-top right
将位于锚点的右侧,并从锚点的底部到定位元素的原始包含块的顶部。
下拉菜单的常见位置区域为 position-area: block-end span-inline-end
。
span-all
关键字跨越 3 行或 3 列。
单个关键字
如果您只设置了一个关键字,系统会自动设置另一个轴。这在很大程度上符合您的预期,但了解其工作原理可能会很有用。
如果提供的关键字明确指定了轴,则另一个轴将计算为 span-all
。这意味着 position-area: bottom
等效于 position-area: bottom span-all
,定位的元素将位于锚点下方,并占据包含块的整个可用宽度。
另一方面,如果关键字未明确指明轴,则会重复。position-area: start
等效于 start start
,并且在从左到右书写的语言中位于锚点的左上角。
anchor()
函数
对于更高级的用例,position-area
可能无法满足您的需求。借助 anchor()
函数,您可以根据另一个元素的位置设置各个边衬区属性。这会解析为 CSS 长度,这意味着您可以在计算中使用它,也可以将其与其他 CSS 函数搭配使用。此外,您还可以将不同侧边系到不同的锚点。
anchor()
函数接受锚点名称和锚点侧。如果您的元素具有默认锚点(通过 position-anchor
设置或隐式设置,例如使用 popover),则可以省略锚点名称。
.positionedElement {
block-start: anchor(--my-anchor start);
/* OR */
position-anchor: --my-anchor;
block-start: anchor(start);
}
后备值
如果找不到 anchor()
函数的锚点,整个声明将无效。如果锚点在定位元素之后呈现,或者没有具有匹配 anchor-name
的元素,就可能会发生这种情况。为处理此问题,您可以设置回退长度或百分比。
.positionedElement {
block-start: anchor(--my-anchor, 100px)
}
在上述示例中,定位元素的左侧值锚定到 --focused-anchor
,但该 anchor-name
仅在悬停或聚焦于第一个按钮时存在。由于 anchor()
函数会解析为长度,因此您可以将另一个锚点用作后备。如果我们不提供回退,定位元素将不会被定位。
锚定边关键字
锚定边值用于选择要与哪个锚定边对齐。与 position-area
类似,锚点侧值支持多种不同的语法。
类型 | 值 | 说明 |
---|---|---|
真机 | top 、left 、bottom 、right |
实体关键字与锚点的特定一侧对应,但只能用于与您要设置的位置元素边衬区相同的轴上。 例如, |
侧边 | inside 、outside |
例如, |
逻辑 | start 、end 、self-start 、self-end |
逻辑关键字是指锚点的侧边,具体取决于定位元素的书写模式(使用 |
百分比 | 0% - 100% |
百分比值用于将定位元素放置在指定轴上从锚点开头到结尾的轴上。 |
此示例展示了百分比值如何始终在指定轴上从开头到结尾变化:
使用 anchor()
由于 anchor()
是长度,因此非常灵活。您可以使用 max()
和 calc()
等 CSS 函数来操纵值。
一个限制是,您只能对内边距属性使用 anchor()
函数。
上例在打开的详情面板后面添加了一个背景,当打开其他面板时,该背景会平滑地动画显示,并会拉伸以包含悬停的详情面板。为此,它使用 min()
来选择两个锚点之间的较小长度。
#indicator{
/* Use the smaller of the 2 values: */
inset-block-start: min(
/* 1. The start side of the default anchor, which is the open `<details>` element */
anchor(start),
/* 2. The start side of the hovered `<details>` element. */
anchor(--hovered start,
/* If no `<details>` element is hovered, this falls back to infinity px, so that the other value is smaller, and therefore used. */
var(calc(1px * infinity)))
);
}
该示例还使用 calc()
在打开的面板周围添加内边距。
使用锚点的大小
您还可以使用 anchor-size()
函数将锚元素的尺寸用于定位元素的尺寸、位置或边距。
anchor-size()
接受锚点名称,或使用默认锚点。默认情况下,它将使用所用轴上锚点的大小,因此 width: anchor-size()
将返回锚点的宽度。您还可以使用其他轴,方法是使用物理关键字 width
和 height
或逻辑关键字 block
、inline
、self-block
和 self-inline
指定所需的长度。
处理溢出
您创建了一个下拉菜单组件,并使用锚点定位将下拉菜单放置在您希望的位置。但随后,您将菜单移到了屏幕的另一侧,或者将其用于用户菜单,而用户的名字非常长。突然,下拉菜单消失在屏幕之外。接下来该怎么做呢?
CSS 锚点定位具有内置系统,可让您在定位元素最终位于其包含块之外时快速构建一组可靠的后备方案。
后备选项
position-try-fallbacks
规则接受回退选项列表。当默认位置溢出时,系统会按顺序尝试每个选项,直到找到不溢出的位置。
您可以使用任何 position-area
值作为后备选项。在此示例中,在从左到右的书写模式(例如英语)下,定位元素将尝试定位在锚点的底部,跨越中间列和右侧列。如果溢出,则会尝试定位在锚点的底部,跨越左列和中间列。如果该值也溢出,即使溢出,位置也会恢复为默认位置。
.positioned-element {
position-area: block-end span-inline-end;
position-try-fallbacks: block-end span-inline-start;
}
还有几个 flip-
关键字可处理常见的回退情况。flip-block
和 flip-inline
尝试翻转块轴和内联轴上的元素。它们还可以与 flip-block flip-inline
结合使用,以沿两个轴翻转。值 flip-start
会使定位元素沿从锚点起始角到结束角的对角线翻转。
您还可以使用 @position-try
创建自定义回退选项,这样您就可以设置边距、对齐方式,甚至更改锚点。
@position-try --menu-below {
position-area: bottom span-right;
margin-top: 1em;
}
#positioned-element {
position-try: --menu-below;
}
可以将 flip-block
和 flip-inline
添加到 @position-try
后备选项中以创建变体。
#positioned-element {
position-try: --menu-below, flip-inline --menu-below;
}
在上述示例中,浏览器会按照以下步骤操作,并在找到不会溢出的解决方案后立即停止。
- 元素放置在锚点的右下角,偏移量为
position-area: end
。 - 如果溢出,则使用名为
--bottom-span-right
的自定义回退选项放置元素,该选项使用position-area: bottom span-right
放置元素,并在下方添加额外的边距。 - 如果溢出,则使用
flip-inline --bottom-span-right
放置元素,该属性将自定义回退选项与flip-inline
(本质上是position-area: bottom span-left
)相结合。 - 如果溢出,系统会使用
--use-alternate
自定义回退选项放置元素,该选项会将其放置在完全不同的锚点下方。 - 如果溢出,即使已知会溢出,元素也会恢复到其原始位置(使用
position-area: end
)。
回退顺序
默认情况下,当初始位置溢出时,浏览器会尝试 position-try-fallbacks
中的每个选项,直到找到不溢出的位置。您可以使用 position-try-order
替换此行为,以测试每个回退选项,并使用在指定轴上具有最大空间的选项。
您可以使用逻辑关键字 most-block-size
和 most-inline-size
,也可以使用物理关键字 most-height
和 most-width
来指定轴。
position-try-order
和 position-try-fallbacks
可与 position-try
简写形式结合使用,且顺序在前。
滚动
用户在滚动时,希望网页能够流畅地移动。为此,浏览器对滚动时锚点定位的使用方式进行了限制。
虽然您可以将定位元素与不同滚动容器中的锚点相关联,但该元素只会响应其中一个锚点的滚动而移动。这将是默认锚点,即弹出式窗口的隐式锚点或 position-anchor
的值。
您会注意到,即使锚点滚动到视图之外,定位的元素仍保持可见。如需在锚点隐藏时隐藏定位元素,请设置 position-visibility: anchors-visible
。这不仅适用于锚点过度滚动的情况,也适用于以其他方式隐藏锚点的情况,例如使用 visibility: hidden
。
检验您的掌握情况
anchor()
中“side”的有效值有哪些?
inside
25%
25px
25px
等长度作为回退值,但只能使用百分比作为边。block-start
start
哪些是 position-area
的有效值?
top
block-end inline-end
block-start block-end
哪些属性支持 anchor()
函数?
top
margin-left
inset-block-start
transform
如果存在多个具有相同 anchor-name
的锚点,会发生什么情况?