aria-rowindex 不该用在瀑布流里——它专用于 grid/treegrid 等有明确行列语义的组件,而瀑布流是流式布局,无稳定行概念;应改用 role="list" + aria-posinset/aria-setsize 表达列表序位。

aria-rowindex 不该用在瀑布流里——它属于 ARIA 规范中为 grid / treegrid 这类**有明确行列坐标语义的表格型组件**设计的属性,而瀑布流(WaterFlow、CSS column-count、JS Masonry)本质是**流式布局容器**,没有“第几行”这个稳定概念。
为什么 aria-rowindex 在瀑布流中无法可靠计算
瀑布流的“行”是视觉对齐结果,不是 DOM 结构层级:
- 使用
column-count时,浏览器按内容流自动折列,aria-rowindex无从映射到真实渲染位置; - JS 实现(如 Masonry)把每个 item 绝对定位到某列底部,DOM 顺序和视觉行序完全脱钩;
- 滚动过程中,item 可能被复用、卸载、重排,所谓“当前第几行”每帧都在变,无法静态赋值;
- 屏幕阅读器不会按视觉“行”解析瀑布流,而是线性遍历 DOM 顺序——此时强行塞
aria-rowindex="5"反而制造语义混乱。
替代方案:用 role="list" + aria-setsize/aria-posinset 更合理
瀑布流的真实语义是“一个可滚动的项目列表”,不是二维表格。正确做法是放弃行列索引,转而表达“当前项在整个列表中的位置”:
- 给外层容器加
role="list",移除所有role="row"/role="grid"; - 每个
FlowItem或.item加role="listitem"; - 动态更新
aria-setsize(总项目数)和aria-posinset(当前项序号),例如:<div role="listitem" aria-setsize="127" aria-posinset="42">... <li>懒加载新数据后,批量更新新插入项的 <code>aria-posinset,无需重算全部——只要保证序号连续、不重复、不跳空即可。 - 需维护一个列高数组(如
[240, 310, 195]),每次新增/重排后调用getBoundingClientRect()扫描所有可见 item 的top值,再按列分组聚类; - “第几行”只能近似为
Math.round(itemTop / avgRowHeight),但平均行高本身就不稳定; - 滚动中频繁调用
getBoundingClientRect()会触发强制同步布局,严重掉帧; -
aria-rowindex必须从 1 开始且连续,但瀑布流顶部常有 header、loading footer,这些节点不能参与计数,逻辑极易漏判。
如果硬要用 grid 语义(不推荐但需兼容旧需求)
仅当瀑布流被强制包裹进 role="grid" 且必须暴露行列信息时,才考虑模拟计算,但代价高、易出错:
真正需要无障碍支持的瀑布流,重点不在“告诉屏幕阅读器这是第几行”,而在确保焦点可入、顺序合理、加载状态可感知。用 role="list" + aria-posinset 是目前唯一被主流读屏器(NVDA、VoiceOver、TalkBack)稳定支持的路径。强行套用 aria-rowindex 不仅无效,还会掩盖更基础的语义缺失问题。











