position:sticky失效问题剖析(Position: analysis of sticky failure)

一、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,且参考滚动父级元素的高度要比内部元素小

搜索

复制

————————

1、 Position: sticky positioning is similar to relative positioning except the offsets are automatically calculated in reference to the nearest scrollport Generally speaking, sticky positioning is similar to relative positioning. It is relative positioning before the sticky feature is triggered. When it is expressed as the sticky feature, it is fixed positioning, and the offset will be automatically calculated according to the nearest scrollport. Among them, a * * very important concept is nearest scrollport, It means that the sticky element will be positioned relative to its nearest scrollport * * before it disappears.

二、正常的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>

In this demo, first check that the scrolling parent element referenced by box2 that sets the sticky attribute is body, but in fact, the reason why body scrolls is because the height of box1, the parent element of box2, stretches the body, so the top set by box2 is relative to the body

2、 Position: stick effectiveness and invalidation principle 2.1 reference rolling parent element is the parent container of body (1) package. If overflow: hidden is set, it will become invalid

.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>

In this demo, first check that the scrolling parent element referenced by box2 that sets the sticky attribute is body, but in fact, the reason why body scrolls is because the height of box1, the parent element of box2, stretches the body, so the top set by box2 is relative to the body, but sticky still fails. The reason for the failure is that box1, the parent element of box2, sets overflow: Hidden / Auto / scroll / overlay, At this time, the scrolling element referenced by the box2 element with the sticky attribute set is not body, but box1, and the height of box1 is much higher than that of box2, which is not enough to enable box1 to scroll. Therefore, it cannot be used as the reference parent scrolling element of box2, and it becomes invalid

So think about a question. In the case of multi-level, instead of setting overflow on its direct parent element: Hidden / Auto / scroll / overlay, but setting overflow on its ancestor element will affect the sticky attribute of the child element? The answer is: Yes, it will. For the same reason, as long as the referenced parent scrolling element cannot scroll, the element with sticky set inside will not take effect

.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>

Therefore, the overflow attribute of any parent node set as position: sticky element must be visible, otherwise position: sticky will not take effect. (2) the height of the nearest parent container of the package is less than or equal to the height of the sticky element will fail. In the case of multi-level nesting, such as the legend, the actual reference scrolling parent element of box is body, but there are still several parent structures in the outer layer: child, parent Container (the actual application scenario can be regarded as setting the outer layout layout of the sticky element). At this time, note that if the height of the nearest parent container of the package is less than or equal to the height of the sticky element, it will not take effect. It is reasonable that the box should be located in the reference rolling parent element body, but it is not possible. Although the actual reference rolling parent element of the box is body, However, the set sticky element box still depends on the nearest parent container child wrapped by it to scroll. After the child’s height scrolls, the sticky attribute of box will also become invalid

.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; }

As shown in the figure above, the sticky element The direct parent element of box The child height is not larger than the sticky element, so the sticky effect cannot be achieved

Think about another question. In the actual multi-level situation, instead of adding a height larger than the sticky element to its direct parent, can the sticky effect be achieved by adding a height smaller than the sticky element to its ancestor element? The answer is: Yes

.container{ width: 700px; height: 2900px; margin-top: 200px; background-color: bisque; padding-top: 80px; /* overflow: auto; */ } . Parent {padding top: 80px; width: 600px; height: 500px; / / the height of the ancestor element is set to 500px background color: Aqua;} Child {width: 500px; padding top: 50px; height: 1600; / / set the height of the nearest parent element to 1600px background color: red;} box{ width: 500px; height: 200px; background-color: blueviolet; position: sticky; top: 0; } 2.2 the reference scrolling parent element is an internal element set by itself. This should be distinguished from the above reference scrolling parent is body. Not all parents above the sticky element cannot set overflow. At this time, the reference scrolling parent element of the sticky element wrap1 is wrap, and the scrolling of wrap is affected by the height of wrap2. The height of wrap2 is higher than wrap. If overflow: Auto / scroll / overlay is not set on the wrap element, wrap cannot scroll. Because wrap can scroll, wrap at this time can be regarded as the reference scrolling parent element of wrap1. The sticky effect of wrap1 element is realized in 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; }

After reading the above demos, you can summarize the effective rules of position: sticky. If you understand the effective rules, you will know why the sticky set sometimes fails:

1. It is necessary to determine one of the four thresholds (top, right, bottom or left) to set the reference scrolling parent element 2 of the sticky element (and reach the set threshold) before the sticky positioning can take effect. Otherwise, its behavior is the same as relative positioning; And when top and bottom are set at the same time, the priority of top is higher, and when left and right are set at the same time, the priority of left is higher. 3. If the reference scrolling parent element of sticky element is body, the overflow attribute of any parent node of the element set to position: sticky must be visible, not hidden / Auto / scroll / overlay, otherwise position: sticky will not take effect; And the height of the nearest parent container of the package is greater than that of the sticky element; If the reference scrolling parent element of the sticky element is an internal element set by itself, the reference scrolling parent element can set overflow: / Auto / scroll / overlay, and the height of the reference scrolling parent element is smaller than the internal element

search

copy