代表性面试主题

前端面试:如何用 CSS sibling-index() 和 sibling-count() 生成自适应序号?

前端中等
Offer.cc 编辑团队发布 更新

题干

列表项数量和位置会变化时,如何用 `sibling-index()` 与 `sibling-count()` 实现 CSS 布局或动画,同时保证旧浏览器、Shadow DOM 和可访问性行为可控?

题干与适用场景

一个响应式卡片列表由 CMS 动态增删。设计要求让每张卡根据兄弟总数计算宽度,并按当前位置延迟动画,同时保留真实的可访问序号。请说明 sibling-index()sibling-count() 的语义、使用边界、降级和验证。

面试官考察点

  • 是否知道两个函数返回整数,位置从 1 开始,作用于 flat tree。
  • 是否区分 CSS 视觉顺序与 DOM/辅助技术顺序。
  • 是否考虑函数尚未 Baseline、@supports、静态回退和性能。
  • 是否能说明 Shadow DOM、伪元素、隐藏节点和动态列表的影响。

回答前需要澄清的问题

  1. 目标是视觉宽度、动画延迟,还是用户必须读到的序号?三者的降级不同。
  2. 列表是否包含隐藏、模板或虚拟化节点?它们是否仍在 flat tree 中?
  3. 支持的浏览器范围和是否允许使用 JavaScript 回退是什么?
  4. 是否使用 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. 用于尺寸和动画

卡片可以按兄弟数量分摊宽度,动画延迟可以按当前位置递增:

css
.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. 可访问性与语义

视觉延迟和宽度不产生可读序号。列表应使用 olli 或明确的 aria-posinsetaria-setsize,并确保屏幕阅读器顺序与 DOM 一致。若 CSS 只用于装饰,禁用动画或函数不应影响内容理解。

6. 动态更新与性能

兄弟增删会改变计算值,浏览器需要重新计算受影响的样式。对数千节点的列表应测量样式重算、布局和动画,而不是只看单个 demo。虚拟化列表可以限制 DOM 数量,但此时函数只能看到窗口内兄弟,不能代表总数据集。

高质量示范回答

我会把 sibling-index()sibling-count() 当作视觉计算能力:前者返回 flat tree 中从 1 开始的位置,后者返回直接子元素总数。卡片宽度和动画延迟可用它们增强,但语义序号仍由 olli 或 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。

公开来源

同类题目