我的立场:粘性元素在使用flexbox时是不粘性的

我被这个卡住了一点,我想我要分享这个position: sticky + flexbox gotcha:

我的粘性div工作良好,直到我切换到flexbox容器的视图,突然div不粘时,它被包装在一个flexbox父。

.flexbox-wrapper {
display: flex;
height: 600px;
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>

JSFiddle显示问题

128062 次浏览

由于伸缩盒元素默认为stretch,所有元素的高度都相同,不能滚动。

align-self: flex-start添加到sticky元素中,将高度设置为auto,这允许滚动,并修复了它。

目前,在所有主要浏览器中,这是支持,但Safari仍然落后于-webkit-前缀,而除了Firefox之外的其他浏览器在position: sticky表方面有一些问题。

.flexbox-wrapper {
display: flex;
overflow: auto;
height: 200px;          /* Not necessary -- for example only */
}
.regular {
background-color: blue; /* Not necessary -- for example only */
height: 600px;          /* Not necessary -- for example only */
}
.sticky {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 0;
align-self: flex-start; /* <-- this is the fix */
background-color: red;  /* Not necessary -- for example only */
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>

JSFiddle显示解决方案

你也可以尝试添加一个子div到包含内容的伸缩项中,并将position: sticky; top: 0;赋值给它。

这适用于我的两列布局,其中第一列的内容需要保持粘性,第二列显示可滚动。

在我的例子中,其中一个父容器应用了overflow-x: hidden;,这将破坏position: sticky功能。您需要删除该规则。

任何父元素都不应该应用上述CSS规则。这个条件适用于'body'元素之前的所有父元素。

我做了一个临时的折叠桌,但遇到了这个问题。为了解决这个问题,我简单地把粘性标题行放在flexbox的外面,就在它之前。

对于我的情况,align-self: flex-start(或justify-self: flex-start)解决方案不起作用。我也需要保留overflow-x: hidden,因为有些容器是水平滑动的。

我的解决方案需要用overflow-y: auto嵌套display: flex来获得所需的行为:

  • header可以动态调整高度,这阻止了使用position: absoluteposition: fixed
  • 内容垂直滚动,水平约束为视图宽度
  • 粘滞元素可以在垂直的任何地方,粘在标题的底部
  • 其他元素可以水平滑动
    • 看起来SO代码片段工具不能在子元素上渲染width以正确地演示水平滑动,或者可能在我的实际布局中有一些其他设置使其工作…
    • 请注意,为了允许overflow-x: auto在带有overflow-x: hidden的父元素下的元素中正确工作,需要一个不做任何其他事情的包装器元素

body {
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
}


body>header {
background-color: red;
color: white;
padding: 1em;
}


.content {
overflow-x: hidden;
overflow-y: auto;
display: flex;
flex-direction: column;
}


article {
position: relative;
display: flex;
flex-direction: column;
overflow-y: auto;
}


.horizontal_slide {
display: flex;
overflow-x: auto;
background-color: lightblue;
padding: .5em;
}


.horizontal_slide>* {
width: 1000px;
}


.toolbar {
position: sticky;
top: 0;
z-index: 10;
background-color: lightgray;
padding: .5em;
display: flex;
}
<header>Fancy header with height adjusting to variable content</header>
<div class="content">
<article class="card">
<h1>One of several cards that flips visibility</h1>
<div class="overflow_x_wrapper">
<div class="horizontal_slide">
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
<div>Reason why `overflow-x: hidden` on the parent is required
</div>
</div>
<div class="toolbar">Sticky toolbar part-way down the content</div>
<p>Rest of vertically scrollable container with variable number of child containers and other elements</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</article>
</div>

从我的经验来看,全局解决方案/规则:

不要将含有粘性元素的结构直接放在{display: flex}容器中。

相反(对于flex布局)将带有粘性元素的表/div放在flex sub-container中(例如,使用{flex: 1 1 auto},但是没有 {display: flex}),那么一切都应该按预期工作。

如果你在父元素中使用flex,那么在你想要设置为sticky的元素中使用align-self: flex-start

position: sticky;
align-self: flex-start;
top: 0;
overflow-y: auto;
确保flex-wrapper已经分配了高度,并将溢出值设置为auto。 然后添加“align-self: flex-start”;

.flexbox-wrapper {
display: flex;
height: 600px;  //<- nessary,and don't assign with %
overflow:auto;  //<-fix
}
.regular {
background-color: blue;
}
.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: red;
align-self: flex-start; // <-fix
}
<div class="flexbox-wrapper">
<div class="regular">
This is the regular box
</div>
<div class="sticky">
This is the sticky box
</div>
</div>