使用 CSS 锚点定位构建提示或弹出式窗口时,如果元素会溢出视口,浏览器可以自动将元素“翻转”到后备位置。发生这种情况时,您可能需要调整定位内容的样式,例如重新定位从定位内容指向锚点的箭头。
锚定容器查询解决了这个问题,它允许您查询元素的有效定位状态,并相应地应用样式。如需了解具有视口碰撞翻转(不重新设置方向箭头样式)的常规锚定下拉菜单和弹出式菜单定位,请参阅视口感知下拉菜单。
问题
假设有一个提示,默认显示在锚点上方。底部有一个“向下”箭头。如果用户滚动屏幕,并且提示翻转到锚点下方,则箭头现在指向错误的方向,并且位于提示的错误一侧。
解决方案:锚定容器查询
通过在定位元素上设置 container-type: anchored,您可以将其转换为查询容器,该容器会了解其自身的锚点定位状态。然后,您可以使用 @container anchored() 查询来更新其后代或伪元素。
1. 创建提示和触发器
使用 Popover API 创建提示。通过 popovertarget 调用弹出式窗口会自动与触发器按钮建立隐式锚点连接,因此无需显式 anchor-name 或 position-anchor。
<button popovertarget="tooltip" id="anchor" aria-describedby="tooltip">anchor</button>
<div id="tooltip" popover role="tooltip"></div>
重置弹出式窗口边衬区和外边距样式,以便与锚点定位搭配使用,但仅在支持锚点定位时适用。
@supports (anchor-name: --my-anchor) {
[popover] {
inset: auto;
margin: unset;
}
}
2. 设置容器
将 container-type: anchored 应用于要定位的元素。此元素还必须定义 position-try-fallbacks 才能启用翻转行为。
#tooltip {
position: fixed;
position-area: block-start;
position-try-fallbacks: flip-block;
/* Enable anchored container queries */
container-type: anchored;
}
3. 根据后备样式设置样式
使用 @container anchored(fallback: <value>) 可在特定后备处于活动状态时应用样式。
与所有容器查询一样,@container 只能为容器的后代设置样式。创建箭头的常见策略是使用 ::before 和 ::after 伪元素,这些元素会被视为后代,可以直接设置样式。不过,为了设置提示本身的样式(如第 4 步所示),我们将向提示添加一个子元素,并在其 ::before 伪元素中创建箭头。
<div id="tooltip" popover role="tooltip">
<div class="tooltip-content">Tooltip</div>
</div>
.tooltip-content::before {
/* Default "down" arrow for the 'top' position */
content: "▼";
position: absolute;
inset-block-end: 0;
inset-inline-start: 1rem;
}
/* Update to an "up" arrow when the 'flip-block' fallback (bottom) is active */
@container anchored(fallback: flip-block) {
.tooltip-content::before {
content: "▲";
inset-block-start: 0;
inset-block-end: auto;
}
}
4. 设置容器本身的样式
如果您需要在容器翻转时更改容器本身的属性(例如 margin 或 background-color),则应使用内部封装容器元素。
- 将
container-type: anchored应用于外部定位元素。 - 在
@container块内定位内部元素。
@container anchored(fallback: flip-block) {
.tooltip-content {
border-radius: 0 0 .5rem .5rem;
margin-block-start: 0.25rem;
}
}
最佳做法
- 首选逻辑后备:在
position-try-fallbacks中使用flip-block和flip-inline等关键字,以便自动处理 RTL 和不同书写模式的更简单查询。 - 使用伪元素创建箭头:提示箭头纯粹是装饰性的,非常适合使用
::before或::after,它们可以通过锚定容器查询进行样式设置,而无需额外的 DOM。
后备策略
根据应用的后备位置来定位箭头是一种渐进增强,没有其他方法可以对后备位置做出反应。如需在不支持锚点位置容器查询的浏览器中隐藏箭头,请使用 @supports (container-type: anchored) 测试 CSS 支持情况。
@supports (container-type: anchored) {
.tooltip-content::before {
content: "▼";
}
}
Popover 的回退和浏览器支持
Popover API 主要是一种渐进增强技术,但其核心行为(顶层提升、轻触关闭和 popovertarget 调用)没有仅限 CSS 的等效项。旧版浏览器需要 polyfill,如果您不想提供 polyfill,则需要手动后备方案。
Polyfill:如需在旧版浏览器中支持 popover 属性,请有条件地加载 @oddbird/popover-polyfill。强制性要求:通过检查 HTMLElement.prototype 上的 popover 属性来检测功能,并且仅在缺少原生支持时加载 polyfill,请勿无条件加载。
使用打包工具或导入映射:
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
如果没有打包器,请从 <script type="module"> 内的 CDN 导入:
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
样式注意事项:polyfill 无法定义真正的 :popover-open 伪类,因此它会改为应用 .\:popover-open 类。强制性:使用 :is() 或 :where() 将两者结合,否则缺少 :popover-open 的浏览器会舍弃整个规则:
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
或者,对于没有 polyfill 的旧版后备方案,请使用 position: fixed 并通过 getBoundingClientRect() 手动计算坐标,或依赖 inset: auto 的默认定位(如果该用例可以接受)。
不支持 Popover API 的浏览器也不支持锚点定位,因此提示会显示在屏幕中央。