先记住这个答案
position: sticky 不是相对视口吸附,而是相对离它最近的滚动容器。overflow 取 hidden、auto、scroll 时,这个元素都会成为滚动容器。父元素设了 overflow: hidden 以后,sticky 就把它当成参照物,可这个父元素本身不滚动,滚动的是外面的页面,所以 sticky 的阈值永远不会被触发,看起来就像失效了。修复办法是把 hidden 换成 overflow: clip,或者去掉这层 overflow,让 sticky 能找到真正在滚的那个容器。
sticky相对最近的滚动容器吸附,不是相对视口overflow: hidden会建滚动容器- 横向隐藏溢出可改变纵向计算值
- 可用
overflow: clip避免滚动容器
复现一下失效的场景
一个很常见的布局:外层为了防止内容撑出圆角,加了 overflow: hidden,里面放了一个想吸顶的标题。
overflow: hidden 标题就不吸顶了HTML<style>
.card { overflow: hidden; border-radius: 12px; } /* 罪魁祸首 */
.title { position: sticky; top: 0; background: #fff; }
.content { height: 2000px; }
</style>
<div class="card">
<h2 class="title">分组标题</h2>
<div class="content">很长的内容</div>
</div>
<!-- 页面往下滚,.title 跟着卡片一起滚走,不会停在顶部 -->
<!-- 把 overflow: hidden 改成 overflow: clip,.title 恢复吸顶 -->.card 高度被内容撑开,没有溢出,也就永远不会产生滚动。sticky 的判断是「在 .card 的可视区域里,标题离 .card 顶部是否小于 0」,这个条件始终不成立,所以它一直停在原位,跟着卡片一起被页面滚走。
几种修复方式怎么选
先在开发者工具里从 sticky 元素往上逐层检查 overflow,找到第一个不是 visible 的祖先,它就是当前的参照物。然后按场景选修法。
- 按原意选择修法
- 如果那层
overflow: hidden只是为了裁掉圆角或防止溢出,直接换成overflow: clip,效果基本一样。如果是为了防整页横向滚动条,把overflow-x: clip加在html或body上。如果确实需要一个局部滚动区域,比如表格区域内部滚动,那就给这个容器设固定高度加overflow: auto,让它真的滚起来,sticky会在它内部正常吸顶。实在去不掉的,最后的办法是用IntersectionObserver监听标题是否离开视口,再切换成position: fixed。
容易答错的地方
- 以为
sticky永远相对视口 - 只有当最近的滚动容器就是页面时,效果才等于相对视口。中间任何一层
overflow不是visible或clip,参照物就变了。 - 以为只写
overflow-x: hidden不影响纵向 overflow-x: hidden会把overflow-y从visible连带改成auto,元素在两个方向上都成了滚动容器。很多「整站吸顶突然失效」都是有人在body上加了这一行。- 把问题怪到
overflow上,漏了其他原因 sticky不生效还可能是:没写top等阈值;父元素高度和sticky元素一样高,没有移动空间;在 flex 容器里被align-items: stretch拉满了高度。排查时这几项都要看。
面试官还会怎么问?
sticky 在滚动容器内部吸顶,但还是会被带走,为什么?
sticky 元素不能超出自己父元素的范围。父元素滚出视口时,它会被父元素的底边推着一起离开。这是设计好的行为,用来实现分组标题一个顶走一个的效果。
overflow: clip 和 overflow: hidden 除了 sticky 还有什么区别?
clip 不创建滚动容器,所以不能用脚本滚动,也不会建立新的块格式化上下文。hidden 会创建 BFC,常被拿来清除浮动、阻止 margin 折叠,换成 clip 这些副作用就没了,替换时要确认原来是不是依赖了这一点。另外 clip 可以配合 overflow-clip-margin 让裁剪边界往外放一点。
表格表头吸顶怎么做?
给表格外层包一个有固定高度、overflow: auto 的容器,然后对 th 设置 position: sticky 和 top: 0。把 sticky 设在 th 上比设在 thead 或 tr 上兼容性更稳。记得给 th 设背景色,不然滚动时下面的行会透出来。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。