th加sticky不固定的根本原因是缺少可滚动祖先容器,需满足四个条件:设置overflow-y:auto的父容器、table-layout:fixed、每个th单独加sticky、显式z-index和background-color。

直接加 position: sticky 到 th 上,但 90% 的失败不是因为写错了,而是滚动上下文没配对、列宽没锁死、或者 z-index 被盖住了。
为什么 th 加了 sticky top-0 却不固定?
根本原因:浏览器找不到“可滚动的祖先容器”。position: sticky 不是相对于视口,而是相对于最近一个设置了 overflow-y: auto(或 scroll)且有明确高度/最大高度的父容器。
- 包裹表格的
div没设overflow-y: auto,或者设了但高度不够(比如只写了height: fit-content) - 该容器或其任意上层祖先加了
transform、filter、will-change—— 这些会创建新层叠上下文,直接切断 sticky 链 - 父容器设了
overflow: hidden,哪怕只是临时调试加的,也会让 sticky 完全失效 -
thead本身加sticky没用,必须给每个th单独加
table-layout: fixed 是硬性前提,不是可选项
默认的 table-layout: auto 会让浏览器等所有内容渲染完再算列宽,而 sticky 定位是在布局阶段计算的——两者时间错位,导致表头和 tbody 列宽不一致、错位、甚至闪动。
- 必须显式声明
table { table-layout: fixed; width: 100%; } - 列宽优先用
标签定义,比在th上写width更稳定(尤其在 Tailwind 中,table-fixed类必须配合w-或min-w-才能生效) - 长文本要加
white-space: nowrap或truncate,否则换行会撑破列宽,破坏fixed布局
z-index 和背景色不是锦上添花,是刚需
没有 z-index,tbody 滚动时内容会从表头下方透出;没有 background-color,滚动中会出现视觉撕裂或文字重叠。
-
th必须带z-index: 10或更高(Safari 尤其敏感,建议z-50) -
th必须设background-color(哪怕只是bg-white),否则滚动时右侧内容会“穿”上来 - 如果表格有边框,建议加
border-b,避免表头和第一行数据之间出现空白缝
移动端 Safari 的几个隐藏雷区
iOS 15.4 及更早版本对 sticky 触发条件更苛刻,很多在 Chrome 正常的代码,在 Safari 里会闪退成 static。
- 包裹容器别用
h-[400px],改用min-h-[400px],防止地址栏缩放导致高度重算失败 -
z-index必须显式写在th上,不能靠继承;值建议 ≥ 50,低于 20 在某些 iOS 版本里会被忽略 - 避免在
th内使用display: flex或display: grid,会干扰 sticky 的定位锚点 - 不要依赖
safe-area-inset自动适配,它可能延迟触发 sticky,建议手动加pt-safe类预留空间
真正卡住人的从来不是“怎么写”,而是“为什么写了却不生效”——四个条件(可滚动容器 + table-layout: fixed + sticky 在单元格而非容器 + 显式 z-index 和背景色)缺一不可,漏掉任何一个,表头就只会在滚动瞬间闪一下。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











