深入理解position sticky粘性定位的计算规则

这篇文章发布于 2020年03月7日,星期六,21:16,归类于 CSS相关。 阅读 49171 次, 今日 8 次 24 条评论

 

本文内容接自之前一篇关于sticky粘性定位文章:“杀了个回马枪,还是说说position:sticky吧”。

因此,如果上面一篇文章还没阅读过,先读一读上面文章。

一、从一个现象说起

缩略示意图

设置了position:sticky粘性定位的元素的父元素如果高度计算值和粘性定位元素一样高,则垂直滚动的时候,粘性定位效果是不会出现的。

很多人不理解为什么会这样?

要讲清楚这个问题,就必须深入理解粘性定位的计算规则。

二、深入理解粘性定位的计算规则

粘性定位中有一个“流盒”(flow box)的概念,指的是粘性定位元素最近的可滚动元素(overflow属性值不是visible的元素)的尺寸盒子,如果没有可滚动元素,则表示浏览器视窗盒子。

粘性定位中还有一个名叫“粘性约束矩形”的概念,表示的是粘性定位元素的包含块在文档流中呈现的矩形区域和流盒的四个边缘在应用粘性定位元素的lefttoprightbottom属性的偏移计算值后的新矩形的交集。

由于滚动的时候,流盒不变,而粘性定位元素的包含块跟着滚动,因此粘性约束矩形随着滚动的进行是实时变化的。

假设我们的粘性定位元素只设置了top属性值,则粘贴定位元素碰到粘性约束矩形的顶部的时候开始向下移动,直到它完全包含在粘贴约束矩形中。

下面我们根据一个简单的例子来进一步理解上面的规则,例如,有一个页面是窗体滚动,包含<div>元素和<nav>元素,这两个元素是父子关系,HTML代码如下:

<div>
  <nav>导航</nav>
</div>

其中:

div {
    height: 100px;    
    margin-top: 50px;
    border: solid deepskyblue;
}
nav {
    position: sticky;
    top: 20px;
    background: lightskyblue;
}

则随着滚动的进行,<nav>元素的粘性约束矩形范围和实际的渲染表现如图1所示:

图1 top方位的粘性定位原理示意图

详细的计算规则如下:

由于<nav>这个粘性定位元素的top偏移是20px,因此,流盒矩形就是滚动窗口矩形再往下偏移20px,就是图中的红色色块区域。而<nav>这个粘性定位元素的包含块就是其父元素<div>元素(设置了边框),粘性约束矩形指的是流盒矩形和包含块的重叠区域,因此,图所示的粘性约束矩形就是红色色块区域和方框区域重叠的矩形区域。

然后:

  1. 在默认状态下(图1左一),由于<div>元素设置了margin-top:50px,因此,<nav>这个粘性定位元素的顶部距离粘性约束矩形的顶部还有33px的大小,不会有粘性效果。

  2. 随着浏览器滚动,<nav>元素和粘性约束矩形的顶部距离越来越小,直到为0。此时<nav>元素开始下移,使自己约束在粘性约束矩形范围内,如图1左二所示。

  3. 浏览器继续滚动,<nav>元素的底部也快要超出粘性约束矩形范围的限制了,如图1右二所示。

  4. 最终,<nav>元素的底部和粘性约束矩形范围的底部重合,由于粘性定位元素不能超出粘性约束矩形的范围限制,因此此时粘性效果失效,<nav>元素被跟着一起滚走了,如图1右一所示。

如果还不是很理解,您可以狠狠地点击这里:帮助理解粘性定位的学习页面

rightleft以及bottom方向的定位与top方向类似。

明白了粘性定位的计算规则,也就明白为什么粘性定位元素父元素和自身高度计算值一样的时候没有粘性效果了。因为此时包含块高度和粘性定位元素一样,导致粘性约束矩形的高度最大也就是和粘性定位元素一样高,粘性定位元素的已经完全没有了实现粘性效果的空间。

三、粘性定位其他特征

本文一开始那篇sticky文章还提到了粘性定位其他两个特征:

  • 如果我们的粘性定位元素的某个祖先元素的overflow属性值不是visible,那么页面滚动的时候就不会有粘性定位效果。
  • 同一容器中多个粘贴定位元素独立偏移,因此可能重叠;位置上下靠在一起的不同容器中的粘贴定位元素则会鸠占鹊巢,挤开原来的元素,形成依次占位的效果。

第一个特性源自粘性定位的定义,这么设计主要是为了避免当多个滚动互相嵌套的时候,粘性定位混乱。

第二个特性原因就是本文提到的粘性定位的计算规则,具体解释:

当我们的粘性定位元素都在一个容器的时候,大家都公用一个巨大的粘性约束矩形,因此,滚动的时候会一个一个往上重叠。

当我们的粘性定位元素属于不同容器的时候,就会有多个不同的粘性约束矩形,这些粘性约束矩形正好一个一个排列得很整齐,于是视觉上达成一个巧合一般的约定,即上一个粘性定位元素被滚走,下一个粘性定位元素正好开始有粘性效果。

以上~

感谢阅读,感谢分享!

(本篇完)

分享到:


发表评论(目前24 条评论)

  1. luker说道:

    1. 在默认状态下(图1左一),由于元素设置了margin-top:50px,因此,这个粘性定位元素的顶部距离粘性约束矩形的顶部还有33px的大小,不会有粘性效果。

    上面原文中,此时图1的粘性约束矩形不就是div吗?粘性定位元素nav的顶部距离粘性约束矩形div的顶部不是为0吗?

  2. 不得不错说道:

    这里是不是有问题:
    如果我们的粘性定位元素的某个祖先元素的overflow属性值不是visible,那么页面滚动的时候就不会有粘性定位效果。
    事实上scroll 也是可以的,也必须可以。

  3. Fong。说道:

    我搞懂了,弹性布局水平排列时,align-items 默认值是 stretch,子项高度是跟其包含块高度容器一致的,没有背景填充颜色还真的不容易看出来,这就导致如你所说的”粘性定位元素的已经完全没有了实现粘性效果的空间”。

  4. Fong。说道:

    我看见网上解释 position: sticky 失效都是基于流模型。弹性盒模型对象主轴为水平方向的子元素失效,需设为 align-self: flex-start; 小白求教,能解释下吗。

  5. daxiang说道:

    楼主 这个功能有没有办法在ie上实现 我们现在有需求

  6. 梅花、柒说道:

    td粘性定位position: sticky不会对border起作用,border还是留在原来位置(背景会跟随td一起定位),我测试过div,div的border会跟随div一起定位,应该是和td的border特殊性有关,麻烦看到信息可以对这个问题进行讨论,并且给出一个可靠的解决方案,蟹蟹

  7. 800说道:

    “粘性约束矩形”能不能注明英文

  8. 匆匆说道:

    大神,第二点中的demo 如果把 top: 20px 改为 bottom:20px ,就没有效果了,这是为什么呢?

  9. catee说道:

    流盒的定义有点 没看懂,,,能否具体解释下?

  10. catee说道:

    如何定义流盒的区域?

  11. yu说道:

    真棒

  12. flow box 为什么是红色区域说道:

    红色区域给了一个 .flow-box,指红色区域是流盒
    但按照流盒的定义,离粘性元素最近的最近的可滚动元素(overflow属性值不是visible的元素),在这个例子里,流盒不该是body吗?
    红色区域没有设置overflow,默认值应该是visible;fixed 在页面中,也不能滚动的样子,疑惑这个流盒是怎么判定的

  13. 喝西瓜汁说道:

    没太懂这个”流盒“的作用,去掉这个流盒,效果好像也一样。sticky元素的top值设置到底是相对于谁的

  14. azy说道:

    多谢分享,很实用。

  15. ceshi说道:

    “这个粘性定位元素的顶部和粘性约束矩形的顶部还有33px的距离”,这句话有歧义,容易理解成“这个粘性定位元素的顶部与粘性约束矩形的顶部还有33px的距离”,让人分不清粘性约束矩形指的时div还是红色区域,应该是:这个粘性定位元素和粘性约束矩形的顶部距离流盒还有33px的距离

  16. Neal说道:

    然后当你想用在thead时发现不行,以为踩到小坑或者自己姿势不对。
    然后你就会发现一个大坑
    非常有趣

  17. uiueux说道:

    多谢分享,困扰了好久,今天才发现是因为祖父元素有overflow不可见导致的

  18. oh,no说道:

    这期太粗糙了,连兼容性都懒得写