一、position:sticky生效的原理

在 W3 官方文档中的定义是:Sticky positioning is similar to relative positioning except the offsets are automatically calculated in reference to the nearest scrollport.
通俗来说就是,Sticky 定位类似于相对定位relative,在没有触发sticky特性前是relative定位,当它表现为 sticky的特性时,是fixed定位,会根据最近的滚动容器(nearest scrollport)自动计算偏移量,其中有一个**非常重要非常重要非常重要的的概念就是 nearest scrollport,它表示 sticky 元素在即将消失前会相对它最近的 scrollport 去做定位**。

二、正常的Demo

.box1{
    width: 300px;
    height: 1200px;
    margin-top: 200px;
    padding-top: 100px;
    background-color: aqua;
  }
  .box2{
    width: 200px;
    height: 300px;
    background-color: red;
    position: sticky;
    top: 0;
  }
  <div class="box1">
   	<div class="box2"></div>
  </div>

在这里插入图片描述
在此demo中,首先查看设置sticky属性的box2参考的滚动父级元素是body,但是实际上body之所以滚动,是因为box2的父级元素box1的高度撑开了body,所以box2设置的top是相对于body的

二、position: stick生效与失效原理

2.1 参考滚动父级元素是body

(1) 包裹的父容器设置了 overflow: hidden会失效

.box1{
    width: 300px;
    height: 1200px;
    margin-top: 200px;
    padding-top: 100px;
    overflow: auto;
    /* overflow: hidden;
    overflow: scroll;
    overflow: overlay; */  //父元素设置了除overflow:visible以外其他属性,都会使sticky失效
    background-color: aqua;
  }
  .box2{
    width: 200px;
    height: 300px;
    background-color: red;
    position: sticky;
    top: 0;
  }
 <div class="box1">
   box1
    <div class="box2">box2</div>
  </div>

在这里插入图片描述
在此demo中,首先查看设置sticky属性的box2参考的滚动父级元素是body,但是实际上body之所以滚动,是因为box2的父级元素box1的高度撑开了body,所以box2设置的top是相对于body的,但是sticky还是失效了,失效的原因在于box2的父级元素box1设置了 overflow: hidden/auto/scroll/overlay ,此时设置sticky属性的box2元素参考的滚动元素就不是body了,而是box1,而box1的高度比box2的高度大得多,并不足以让box1实现滚动,所以无法作为box2的参考父级滚动元素,进而失效了

那么思考一个问题,在多层级的情况下,并不是在其直接父元素上设置 overflow:hidden/auto/scroll/overlay,而是在其祖先元素上设置overflow,会影响子元素的sticky属性吗?
答案是:会影响的,一样的道理,只要参考的父级滚动元素无法滚动,都是不会让其内部设置sticky的元素生效

.container{
    width: 700px;
    height: 2900px;
    margin-top: 200px;
    background-color: bisque;
    padding-top: 80px;
    /* overflow: auto; */
  }
  .parent{           //box的祖先元素
    padding-top: 80px;
    width: 600px;
    height: 800px;
    overflow: hidden; //在box的祖先元素处设置overflow:hidden
    background-color: aqua;
  }
  .child{            //box的父级元素
    width: 500px;
    padding-top: 50px;
    height: 500px;
    background-color: red;
  }
  .box{
    width: 500px;
    height: 200px;
    background-color: blueviolet;
    position: sticky;
    top: 0;
  }
 <div class="container"> 
  container
    <div class="parent">
      parent
      <div class="child">
        child
        <div class="box">box</div>
      </div>
    </div>
    <div class="borther"></div>
  </div>

在这里插入图片描述
所以,设定为 position: sticky 元素的任意父节点的 overflow 属性必须是 visible,否则 position:sticky 不会生效
(2) 包裹的最近的父容器高度小于等于sticky 元素的高度会失效
在多级嵌套的情况下,如图例,box的实际参考滚动父级元素是body,但是外层还是有几层父级结构child、parent、container(实际应用场景可当做是设置sticky元素的外层layout布局等),此时注意,如果包裹的最近的父容器高度小于等于sticky 元素的高度,也是不生效的,按道理说此时box应该是在参考的滚动父级元素body中进行sticky定位的,但是并不可以,虽然box的实际参考滚动父级元素是body,但是设置的sticky的元素box还是依赖于它包裹的最近的父容器child进行滚动,待child的高度滚动结束,box的sticky属性也会失效

.container{
    width: 700px;
    height: 2900px;
    margin-top: 200px;
    background-color: bisque;
    padding-top: 80px;
    /* overflow: auto; */
  }
  .parent{
    padding-top: 80px;
    width: 600px;
    height: 500px;
    background-color: aqua;
  }
  .child{
    width: 500px;
    padding-top: 50px;
    height: auto; //父元素高度自动计算
    background-color: red;
  }
  .box{
    width: 500px;
    height: 200px;
    background-color: blueviolet;
    position: sticky;
    top: 0;
  }

在这里插入图片描述

上图所示,sticky元素.box的直接父级元素.child高度没有比sticky元素大,无法实现sticky效果

再思考一个问题,在实际多层级的情况下,并不是在其直接父级上加上一个比sticky元素大的高度,而是在其祖先元素上加一个比sticky元素小的高度,可以实现sticky效果吗?
答案是:可以

.container{
    width: 700px;
    height: 2900px;
    margin-top: 200px;
    background-color: bisque;
    padding-top: 80px;
    /* overflow: auto; */
  }
  .parent{
    padding-top: 80px;
    width: 600px;
    height: 500px; //祖先元素高度设置了500px
    background-color: aqua;
  }
  .child{
    width: 500px;
    padding-top: 50px;
    height: 1600; //最近父级元素设置高度1600px
    background-color: red;
  }
  .box{
    width: 500px;
    height: 200px;
    background-color: blueviolet;
    position: sticky;
    top: 0;
  }

2.2 参考滚动父级元素是自己设置的内部元素

此处应该就要和上面的参考滚动父级是body做区分了,并不是在设置sticky元素的上方所有父级都不可设置overflow。此时设置sticky元素wrap1的参考滚动父级元素是wrap,而wrap的滚动是由wrap2的高度影响的,wrap2的高度比wrap高,如果不在wrap元素上设置overflow:auto/scroll/overlay,wrap是无法滚动的。而正因为wrap可以滚动,那么此时的wrap就可以当做是wrap1的参考滚动父级元素,wrap1元素的sticky效果在wrap中实现

<div class="wrap">
  <div class="wrap1">固定头部</div>
   <div class="wrap2">box2</div>
 </div>
 .wrap{
   margin-top: 100px;
   width: 200px;
   height: 500px;
   background-color: blanchedalmond;
   overflow: auto;
  }
  .wrap1{
    width: 100px;
    height: 30px;
    background-color: aqua;
    position: sticky;
    top: 0;
  }
  .wrap2{
    width: 100px;
    height: 2000px;
    background-color: yellow;
  }

在这里插入图片描述

总结

看完上面几个 DEMO,可以好好总结一下 position:sticky 的生效规则,明白了生效规则就会知道为什么有的时候设定的 sticky 会失效:

1、须确定设置sticky元素的参考滚动父级元素
2、指定 top, right, bottom 或 left 四个阈值其中之一(且达到设定的阈值),才可使粘性定位生效。否则其行为与相对定位相同;并且 top 和 bottom 同时设置时,top 生效的优先级高,left 和 right 同时设置时,left 的优先级高
3、如果sticky元素的参考滚动父级元素是body,设定为 position: sticky 的元素的任意父节点的 overflow 属性必须是 visible,不能是hidden/auto/scroll/overlay,否则 position:sticky 不会生效;且包裹的最近的父容器高度要大于sticky 元素的高度;如果sticky元素的参考滚动父级元素是自己设置的内部元素,参考滚动父级元素可以设置overflow :/auto/scroll/overlay,且参考滚动父级元素的高度要比内部元素小

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐