CSS 锚点定位入门指南

本篇依然来自于我们的 《前端周刊》 项目!

由团队成员 嘿嘿 翻译,他的文章风格稳健而清晰,注重结构与逻辑的严谨性,善于用简洁的语言将复杂技术拆解成易于理解的知识点~

欢迎大家 进群 与他探讨 React 最新趋势,并持续追踪全球前端领域的最新动态!

原文地址:A gentle introduction to anchor positioning

锚点定位(Anchor Positioning)允许你根据另一个元素的位置来放置页面上的元素。它能让你仅用 CSS(少写点代码),更轻松地创建响应式的菜单和工具提示(tooltip)。下面我们来看看它是怎么工作的。

假设你在导航栏里有一个头像(avatar),像这样:

带有两个文本选项和一个头像的导航栏

当你点击头像时,希望菜单能显示在它正下方。点击交互部分可以用 Popover API 仅通过 CSS 来 实现。但点击之后,菜单到底该出现在哪里呢?

以前这通常需要 JavaScript 来计算。但现在,有了锚点定位,只需要几行 CSS 就能搞定。锚点定位会根据头像的位置来决定菜单该放哪。

比如,你可能想让菜单出现在头像下方,左对齐,就像这样:

导航栏头像菜单展开并左对齐

或者你想让它挂在头像旁边,在右边开个派对,也行:

导航栏头像菜单展开到头像右侧

你可以把它放在多个位置,不过我觉得第一个例子比较常见,很多网站和 Web 应用里都能见到。我们来看看怎么写代码实现它。

第一步是让菜单知道它对应的头像。

一种很好的理解方式是:把菜单想象成锚定(anchor)在头像上的。这样我们就称头像为 锚点(anchor) ,菜单为 目标(target)

你需要在头像元素上声明一个 anchor-name 来命名锚点。因为头像表示你的用户信息按钮,所以我们给它一个 profile-button 的类名。

代码如下:

css 复制代码
.profile-button {
    anchor-name: --profile-button;
}

接着,在菜单(目标)上声明 position-anchor,它的值就是你刚才声明的 anchor-name。这就建立了连接,让目标知道该跟随哪个锚点。

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
}

最后一步是给菜单一个绝对定位(absolute)或固定定位(fixed),像这样:

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
}

很好,连接建立完成!接下来我们要决定菜单出现的位置。

position-area

其中一种方法是使用 position-area 属性。

position-area 允许你在一个九宫格中放置元素,其中锚点在中间。这个九宫格的范围就是锚点的包含块(containing block)。

九宫格,头像在中间,标注了 left、center、right、top、bottom

你可以用这个网格来决定菜单的位置。想让菜单显示在右上角?写 top right 就行。

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    position-area: top right;
}

九宫格,头像在中间,右上角蓝色标记

但问题是,top right 看似直观,却并不是最佳写法。

在 CSS 中,我们应尽量使用逻辑属性(logical properties),而不是物理属性(physical properties)。这样更具包容性,不会依赖于书写方向或语言。

这是使用逻辑属性的同一个九宫格:

九宫格,头像在中间,标注 start/block-start、center、end/block-end、end/inline-end、start/inline-start

把上面的 top right 改成逻辑写法就是 block-start inline-end

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    position-area: block-start inline-end;
}

九宫格,头像在中间,右上角蓝色标记 block-start inline-end

想让它在下方居中?写成逻辑的 block-end center

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    position-area: block-end center;
}

九宫格,头像在中间,下方居中蓝色标记 block-end center

不过你的菜单比头像宽,所以如果用 block-end center,它会超出网格,看起来就像这样:

去掉网格线后就是这样:

这可不是你想要的效果。如何实现干净的左对齐呢?

你可以把 position-area 设置为 block-end span-inline-end。这样菜单会从头像下方开始,沿着内联方向展开,像这样:

代码如下:

arduino 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    position-area: block-end span-inline-end;
}

完美,正是你想要的!

主要的逻辑值有:start/block-startend/block-endstart/inline-startend/inline-end。如果必须,也可以用物理值:leftcenterrighttopbottom

完整的值列表可以查看 MDN 的文档 这里。接下来让我们继续定位。

桌面端没问题,菜单右边有足够空间。但在移动端,视口更窄,菜单就容易溢出。更好的做法是让菜单右对齐,往左展开。要实现这种切换,我们需要在空间不足时告诉菜单尝试另一个位置。

锚点定位正好支持这一点!这就是它的响应式灵活性。如果定义的位置没空间,它会自动尝试别的位置。这可以通过 position-try 来实现。

示例:

arduino 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    position-area: block-end span-inline-end;
    position-try: block-end span-inline-start;
}

position-try 顾名思义,就是让元素尝试一个新的位置。效果如下:

窗口变窄时,菜单自动从右对齐切换到左对齐。

Anchor()

anchor() 函数提供了另一种定位思路。position-area 是基于九宫格,而 anchor() 则是基于锚点的 边缘 来放置目标。

它只能用于 inset 属性。inset 属性用于通过偏移量来控制元素位置。包括物理属性:toprightbottomleft;逻辑属性:inset-block-startinset-block-endinset-inline-startinset-inline-end;以及简写:inset-blockinset-inline

比如,要让菜单左侧和头像左侧对齐,可以用 left: anchor(left);再让菜单顶端和头像底部对齐,可以用 top: anchor(bottom)

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    left: anchor(left);
    top: anchor(bottom);
}

不过我们还是应该用逻辑属性,改写如下:

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    inset-inline-start: anchor(start);
    inset-block-start: anchor(end);
}

这里,lefttop 被替换成了 inset-inline-startinset-block-start

anchor() 里,你可以选择 startend(基于包含块),或 self-startself-end(基于锚点内容)。

此外,anchor() 还能显式指定锚点名称。如果写成这样:

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    left: anchor(--profile-button left);
    top: anchor(--profile-button bottom);
}

如果不写,默认会用 position-anchor 设置的那个。

更有趣的是,anchor() 可以和 calc() 结合。假设头像容器有 1.25em 的 padding,你只想让菜单对齐头像图片(不包含 padding),可以写成:

css 复制代码
.profile-menu {
    position-anchor: --profile-button;
    position: absolute;
    inset-inline-start: calc(anchor(start) + 1.25em);
    inset-block-start: anchor(bottom);
}

效果如下(粉色线表示对齐):


position-areaanchor() 都能帮助你基于锚点来定位目标。选择哪种方式取决于你更喜欢哪种思维模式。如果你觉得网格更直观,那就用 position-area;如果更习惯基于边缘的精确控制,那就用 anchor()

你可以在 这个 CodePen 上玩一玩,换掉 position-area 的值,看看菜单会怎么移动,或者试试 anchor()

关于锚点定位的更多特性,可以查看 MDN 的详细文档 这里。另外还有个小游戏 Anchoreum,能帮你更轻松地理解锚点定位。

相关推荐
恋猫de小郭6 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端