详细说明:【前端】position:sticky解析 这次应该大结局了 ,补充说明并修正若干概念。
基本效果
来自mdn的例子:position: sticky;top: 20px;
特性描述
看了下MDN和大佬们的讲解,通俗的总结如下:
- 常规情况下同
position:static; - 在显示不超出其父元素的前提条件下,尽可能与 最近滚动祖先 保持指定的最小距离
- 使用了
position:sticky;的元素会为其子元素创建新的层叠上下文(stacking context)
最近滚动祖先 :以position:sticky;元素为起点往上找,遇到的第一个overflow-x或overflow-y不为visible的祖先节点。如果不存在,那么直接取 viewport (即浏览器视窗)。一个简略的查找算法如下:
function getStickyParent(node) {
const parent = node.parentElement;
if (!parent) return null;
const style = window.getComputedStyle(parent);
const overflows = style.overflow.split(" ");
if (overflows.some((o) => o !== "visible")) {
return parent;
}
return getStickyParent(parent);
}
常见误区
后面可看可不看,个人踩坑经验。
对“最近滚动祖先”理解有误
学习的时候有看到说sticky元素的任意祖先元素都不要设置overflow:hidden;,实际上应该是表述有误。
详细描述应该为:如果你想让一个元素sticky到一个祖先元素,那么这个祖先元素要设置overflow为非visible的值,而且要保证它们之间的任意元素都不能有overflow为非visible的值,否则这个元素就会成为你意料之外的“最近滚动祖先”。仅当你需要对 viewport 进行sticky,才需要确保任意祖先元素都没有该值。
没有触发效果
先来搞个demo:
<div style={{ width: 400, height: 400, overflow: "auto" }}>
<div style={{ height: 400, background: "green" }}>
<div style={{ height: 20, width: 20, background: "pink", position: "sticky", top: 0 }} />
</div>
<div style={{ height: 400, background: "red" }} />
</div>
这个很合理,一开始粉色块在没有超出绿色块的前提下,与最外面的“最近滚动祖先”尽可能保持顶部0px。但滚到最后时红色块就把绿色块完全顶开了,此时粉色块就不满足“在显示不超出其父元素的前提条件下”了,所以粉色块只能贴着绿色块的底部被一起往上顶了。
我们给粉色块再套个div#1:
<div style={{ width: 400, height: 400, overflow: "auto" }}>
<div style={{ height: 400, background: "green" }}>
<div id="1">
<div style={{ height: 20, width: 20, background: "pink", position: "sticky", top: 0 }} />
</div>
</div>
<div style={{ height: 400, background: "red" }} />
</div>
这个就是常见的无效了。因为粉色块的父元素变成了div#1,而div#1的高度默认就是粉色块的高度,所以粉色块没啥操作空间,只能从头到尾跟着它的父元素div#1一起被滚动
关于这个条件的原理部分:深入理解position sticky粘性定位的计算规则