position中的sticky

68 阅读4分钟

sticky背景介绍

CSS position 属性用于指定一个元素在文档中的定位方式。top,right,bottom 和 left 属性则决定了该元素的最终位置。

sticky

元素根据正常文档流进行定位,然后相对它的*最近滚动祖先(nearest scrolling ancestor)*和 containing block (最近块级祖先 nearest block-level ancestor),包括 table-related 元素,基于top, right, bottom, 和 left的值进行偏移。偏移值不会影响任何其他元素的位置。 该值总是创建一个新的层叠上下文(stacking context)。注意,一个 sticky 元素会“固定”在离它最近的一个拥有“滚动机制”的祖先上(当该祖先的overflow 是 hidden, scroll, auto, 或 overlay时),即便这个祖先不是最近的真实可滚动祖先。这有效地抑制了任何“sticky”行为(详情见Github issue on W3C CSSWG)。

使用中碰到的问题:

  1. 父级元素不能有overflow:visible以外的overflow属性值设置,否则sticky就会失效
  2. 父级元素和粘性定位元素等高的情况下,sticky就会失效
  3. 设置了sticky属性值的元素,它的父级包含块随着滚动行为,从最近滚动祖先元素范围内消失会把sticky元素带走

static的计算规则

假设存在一个导航场景

HTML如下:

<div class="scroll-body">
    <div class="nav-wrap">
            <div class="nav">导航栏</div>
    </div>
</div>

css如下:

.scroll-body{
    width: 100%;
    height: 200vh;
    background-color: red;
}
.nav-wrap {
    height: 200px;    
    margin-top: 60px;
    border: 3px solid blue;
}
.nav {
    position: sticky;
    top: 20px;
    background: green;
}

效果演示:

动画.gif

从上图中我们能清晰的看到导航栏是如何被.nav-wrap带走的,那么为什么会有这样的行为出现呢?

原来在粘性定位中有流盒(flow-box)的概念,指的是粘性定位元素最近的可滚动元素,如果没有就是浏览器视窗盒子。同时粘性定位中还存在一个“粘性约束矩阵”的概念,它代表的是粘性定位元素的包含块(也就是代码中的.nav-wrap)在文档流中呈现的矩形区域和流盒的四个边缘在应用粘性定位元素的left、top、right、bottom属性的偏移计算值后的新矩形的交集。

简单来说就是,在本示例中由于.nav粘性元素设置了top:20px的偏移量,所以流盒矩形就是滚动窗口矩形再往下偏移20px,流盒矩形与.nav-wrap包含块元素的重合区域就是粘性约束矩阵。 演示过程中效果解析:

  1. 一开始.nav-wrap设置了margin-top:60px,所以.nav的顶部粘性距离top:20px距离60px还有40px的距离不会产生粘性效果。
  2. 随着滚动行为的产生,等到顶部粘性距离达到0的时候,.nav元素开始产生粘性效果,把自己约束在粘性约束矩阵中。
  3. 最后,.nav的元素底部和.nav-wrap元素的底部重合,由于受到粘性约束矩阵的影响,.nav不能超出这个范围,粘性效果失效,只能跟着一起滚动走。

明白了上面这个过程,我们的疑问也就得到了解答。

  1. 父级元素不能有overflow:visible以外的overflow属性值设置,否则sticky就会失效。

    答:因为根据粘性定位的定义,这么设计主要是为了避免当多个滚动互相嵌套的时候,粘性定位混乱。

  2. 父级元素和粘性定位元素等高的情况下,sticky就会失效。

  3. 设置了sticky属性值的元素,它的父级包含块随着滚动行为,从最近滚动祖先元素范围内消失会把sticky元素带走。

    答:因为粘性约束矩阵的存在。粘性定位元素父元素和自身的高度一致时粘性效果就会失效的问题正是因为粘性约束矩阵的存在,相同的高度让粘性效果没有了发挥空间

最后

以下是参考的一些文章