前端面试:如何用 CSS sibling-index() 和 sibling-count() 生成自适应序号?
题干与适用场景
一个响应式卡片列表由 CMS 动态增删。设计要求让每张卡根据兄弟总数计算宽度,并按当前位置延迟动画,同时保留真实的可访问序号。请说明 sibling-index()、sibling-count() 的语义、使用边界、降级和验证。
面试官考察点
- 是否知道两个函数返回整数,位置从 1 开始,作用于 flat tree。
- 是否区分 CSS 视觉顺序与 DOM/辅助技术顺序。
- 是否考虑函数尚未 Baseline、
@supports、静态回退和性能。 - 是否能说明 Shadow DOM、伪元素、隐藏节点和动态列表的影响。
回答前需要澄清的问题
- 目标是视觉宽度、动画延迟,还是用户必须读到的序号?三者的降级不同。
- 列表是否包含隐藏、模板或虚拟化节点?它们是否仍在 flat tree 中?
- 支持的浏览器范围和是否允许使用 JavaScript 回退是什么?
- 是否使用 Shadow DOM、slot 或 CSS counter?这些会改变验证方式。
30 秒回答框架
sibling-count() 返回父级直接子元素总数,sibling-index() 返回当前元素在兄弟中的 1-based 位置,规范按 flat tree 计算。它们适合把数量和位置用于 calc()、网格宽度或动画延迟,但不是可靠的可访问文本来源。先用固定 CSS 回退,再用 @supports 启用新函数;序号交给语义 HTML 或可验证的 counter。测试动态增删、slot、隐藏项、浏览器不支持和 prefers-reduced-motion。
分步骤深入解答
1. 先确认函数返回什么
两个函数都返回整数。sibling-count() 包括元素自身;sibling-index() 从 1 计数。它们统计父元素的直接子元素,规范描述的是 flat tree,因此不能把任意后代元素或视觉排列顺序当成兄弟位置。
2. 用于尺寸和动画
卡片可以按兄弟数量分摊宽度,动画延迟可以按当前位置递增:
.card {
--count: sibling-count();
--index: sibling-index();
flex-basis: calc(100% / var(--count));
animation-delay: calc((var(--index) - 1) * 60ms);
}这是视觉计算,不应替代 DOM 的顺序或业务编号。对空列表、单项和非常大的数量设定边界,避免除零或过长延迟。
3. 处理不支持与降级
MDN 将这些函数标为 Limited availability,不能假定所有生产浏览器支持。先提供固定宽度或 flex 回退,再用 @supports (width: calc(100% / sibling-count())) 逐步增强。无法增强时,JavaScript 只应在业务确实需要动态值时同步更新 CSS 自定义属性,不要让首屏依赖脚本才能可用。
4. flat tree、slot 与隐藏节点
规范以 flat tree 定义兄弟关系;Shadow DOM 的 slot 分发会影响可见树和测试结果。display: none、模板节点、虚拟列表占位符是否成为直接子元素必须用实际 DOM 验证。不要凭视觉顺序推断函数结果,也不要把 order 改变当成索引改变。
5. 可访问性与语义
视觉延迟和宽度不产生可读序号。列表应使用 ol、li 或明确的 aria-posinset 与 aria-setsize,并确保屏幕阅读器顺序与 DOM 一致。若 CSS 只用于装饰,禁用动画或函数不应影响内容理解。
6. 动态更新与性能
兄弟增删会改变计算值,浏览器需要重新计算受影响的样式。对数千节点的列表应测量样式重算、布局和动画,而不是只看单个 demo。虚拟化列表可以限制 DOM 数量,但此时函数只能看到窗口内兄弟,不能代表总数据集。
高质量示范回答
我会把 sibling-index() 和 sibling-count() 当作视觉计算能力:前者返回 flat tree 中从 1 开始的位置,后者返回直接子元素总数。卡片宽度和动画延迟可用它们增强,但语义序号仍由 ol、li 或 ARIA 提供。生产 CSS 先有固定布局回退,再用 @supports 检测函数;对 Shadow DOM、slot、隐藏节点和虚拟化列表做真实 DOM 测试。动态增删和大列表要测样式重算与布局;不支持时保证内容可读,并遵守 prefers-reduced-motion。
常见错误
- 把函数当成普遍可用 → 旧浏览器样式失效 → 提供固定布局和
@supports回退。 - 用视觉
order推断索引 → CSS 顺序不等于 flat tree 顺序 → 以 DOM 和 slot 分发测试。 - 用 CSS 计算替代可访问序号 → 屏幕阅读器得不到位置 → 保留 HTML 语义或 ARIA。
- 虚拟列表直接代表总数量 → 函数只看到当前 DOM 窗口 → 由业务数据提供总数。
- 大列表无限递增动画延迟 → 等待时间和重算成本失控 → 设上限并按 reduced motion 关闭。
追问及应对
sibling-count() 会把后代元素也算进去吗?
不会。它统计父元素的直接子元素,并包含当前元素;后代节点不参与。
如果元素通过 slot 投影,应该按哪个顺序验证?
同时检查 Shadow DOM 的 light tree、slot 分发后的 flat tree 和辅助技术读取顺序。用浏览器实际 DOM 与可访问性树验证,不能只看源码排列。
旧浏览器完全不支持时,能否只用 JavaScript?
可以作为渐进增强,但基础布局和内容必须不依赖脚本。脚本更新 CSS 变量时还要处理增删、SSR 首屏、禁用脚本和 reduced-motion。