我正在研究一个嵌套的flexbox布局,它应该按如下方式工作:
最外层(ul#main
)是一个水平列表,当添加更多项目时,它必须向右展开。如果它变得太大,应该有一个水平滚动条。
#main {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
overflow-x: auto;
/* ...and more... */
}
此列表中的每个项目(ul#main > li
)都有一个标题(ul#main > li > h2
)和一个内部列表(ul#main > li > ul.tasks
)。此内部列表是垂直的,应在需要时包装到列中。当包装到更多列时,其宽度应该增加,以便为更多项目腾出空间。此宽度增加也应适用于外部列表的包含项。
.tasks {
flex-direction: column;
flex-wrap: wrap;
/* ...and more... */
}
我的问题是当窗口的高度太小时内部列表不会换行。我尝试过对所有flex属性进行大量篡改,试图在CSS-Tricks精心遵循指导原则,但没有运气。
此JSFiddle显示了我到目前为止所拥有的内容。
预期结果 (我想要的):
实际结果 (我得到的):
较旧的结果 (我在2015年得到的):
经过一番调查后,这开始变得更加严重。 所有主流浏览器的行为方式相同,并且与我的嵌套Flexbox设计无关。更简单的flexbox列布局拒绝在项目换行时增加列表的宽度。
这other JSFiddle清楚地证明了这个问题。在当前版本的Chrome,Firefox和IE11中,所有项目都正确包装;列表的高度在row
模式下增加,但其宽度在column
模式下不会增加。此外,在更改column
模式的高度时,根本没有立即回流元素,但存在row
模式。
然而,official specs (look specifically at example 5)似乎表明我想做的事情应该是可能的。
有人可以针对这个问题找到解决方法吗?
在调整大小事件期间使用JavaScript进行大量实验来更新各种元素的高度和宽度之后,我得出的结论是,尝试以这种方式解决它太复杂和太麻烦了。此外,添加JavaScript肯定会破坏flexbox模型,该模型应尽可能保持干净。
现在,我回到overflow-y: auto
而不是flex-wrap: wrap
,以便内部容器在需要时垂直滚动。它并不漂亮,但它是一种前进的方式,至少不会过多地破坏可用性。
答案 0 :(得分:133)
这似乎是弹性布局的一个根本缺陷。
列方向的Flex容器不会展开以容纳其他列。 (这不是flex-direction: row
中的问题。)
这个问题已被多次询问(参见下面的列表),CSS中没有干净的答案。
很难将此作为错误,因为问题出现在所有主流浏览器中。但确实提出了这个问题:
所有主流浏览器如何才能获得Flex容器 在行方向上展开但不在列方向上展开?
你会认为其中至少有一个会做对。我只能推测原因。也许这是一个技术上很难的实现,并且被搁置了这个迭代。
更新:此问题似乎已在Edge v16中解决。
OP创建了一个有用的演示来说明问题。我在这里复制它:http://jsfiddle.net/nwccdwLw/1/
Stack Overflow社区的Hacky解决方案:
答案 1 :(得分:5)
晚些时候参加聚会,但几年后仍然遇到此问题。最终找到了使用网格的解决方案。在容器上可以使用
display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);
我在CodePen上有一个示例,可以在flexbox问题和网格修复之间切换:https://codepen.io/MandeeD/pen/JVLdLd
答案 2 :(得分:4)
我刚刚在这里找到了一个非常棒的PURE CSS解决方法。
https://jsfiddle.net/gcob492x/3/
棘手的问题:在列表div中设置writing-mode: vertical-lr
,然后在列表项中设置writing-mode: horizontal-tb
。我必须在JSFiddle中调整样式(删除很多对齐样式,对于解决方案而言这不是必需的。)
注意:评论说它仅在基于Chromium的浏览器中有效,而在Firefox中不可用。我只在Chrome中进行过个人测试。可能有一种方法可以对其进行修改以使其在其他浏览器中运行,或者对上述浏览器进行了更新,以使其能够正常工作。
对此留言大喊:When flexbox items wrap in column mode, container does not grow its width。深入研究该问题线程使我进入https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39,这使我进入了这个JSFiddle。
答案 3 :(得分:1)
不幸的是,许多主要的浏览器都在多年后遭受此bug的困扰。考虑使用Javascript解决方法。每当浏览器窗口调整大小或将内容添加到元素时,请执行此代码以将其调整为适当的宽度。您可以在框架中定义一个指令来为您完成。
element.style.flexBasis = "auto";
element.style.flexBasis = `${element.scrollWidth}px`;
答案 4 :(得分:0)
我以这种方式解决了我的问题,希望它可以帮助其他人偶然发现:
_ensureWidth(parentElement) {
let totalRealWidth = 0;
let parentElementBoundingRect = parentElement.getBoundingClientRect()
let lowestLeft = parentElementBoundingRect.x;
let highestRight = parentElementBoundingRect.width;
for (let i = 0; i < parentElement.children.length; i++) {
let { x, width } = parentElement.children[i].getBoundingClientRect();
if (x < lowestLeft) {
lowestLeft = x;
}
if (x + width > highestRight) {
highestRight = x + width;
}
}
totalRealWidth = highestRight - lowestLeft;
parentElement.style.width = `${totalRealWidth}px`;
}
答案 5 :(得分:0)
解决方法:
在元素加载到屏幕上后,使用 javascript 不难手动设置包装器的宽度。宽度始终是最后一个子元素的右手点。
在 React 中,我根据添加到 flex 包装器的任何子项更新了布局更改,但这可以在您向包装器添加或删除子项的任何时候调用。
let r = refWrapper.current.lastElementChild.getBoundingClientRect()
refWrapper.current.style.width = (r.x+r.width )+'px'
`
其中 refWrapper 是您的 flex 元素
答案 6 :(得分:-1)
由于尚未提出解决方案或适当的解决方法的建议,因此我设法使用了一些不同的方法来获得请求的行为。我没有将布局分为3个不同的div,而是将所有项目添加到1 div中,并在其中添加了一些div来创建分隔。
假设我们有3个部分,则所提出的解决方案是硬编码的,但可以扩展为通用的部分。主要思想是解释我们如何实现这种布局。
:before
,我们可以找到每个部分的标题。space
会在各部分之间形成空白space
不会覆盖该部分的整个高度,因此我还要在这些部分中添加:after
,以便将其定位为绝对位置和白色背景。z-index: -1
。
function calcWidth() {
var size = $(document).width();
var end = $(".end").offset().left;
var todoWidth = $(".doing-first").offset().left;
$(".bg-todo").css("width", todoWidth);
var doingWidth = $(".done-first").offset().left - todoWidth;
$(".bg-doing").css("width", doingWidth);
var doneWidth = $(".end").offset().left - $(".done-first").offset().left;
$(".bg-done").css("width", doneWidth + 20);
}
calcWidth();
$(window).resize(function() {
calcWidth();
});
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
height: 120px;
align-content: flex-start;
padding-top: 30px;
overflow-x: auto;
overflow-y: hidden;
}
.item {
width: 200px;
background-color: #e5e5e5;
border-radius: 5px;
height: 20px;
margin: 5px;
position: relative;
box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.75);
padding: 5px;
}
.space {
height: 150px;
width: 10px;
background-color: #fff;
margin: 10px;
}
.todo-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "To Do (2)";
font-weight: bold;
}
.doing-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "Doing (5)";
font-weight: bold;
}
.doing-first:after,
.done-first:after {
position: absolute;
top: -35px;
left: -25px;
width: 10px;
height: 180px;
z-index: 10;
background-color: #fff;
content: "";
}
.done-first:before {
position: absolute;
top: -30px;
height: 30px;
content: "Done (3)";
font-weight: bold;
}
.bg-todo {
position: absolute;
background-color: #FFEFD3;
width: 100vw;
height: 150px;
top: -30px;
left: -10px;
z-index: -1;
}
.bg-doing {
position: absolute;
background-color: #EFDCFF;
width: 100vw;
height: 150px;
top: -30px;
left: -15px;
z-index: -1;
}
.bg-done {
position: absolute;
background-color: #DCFFEE;
width: 10vw;
height: 150px;
top: -30px;
left: -15px;
z-index: -1;
}
.end {
height: 150px;
width: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="item todo-first">
<div class="bg-todo"></div>
Drink coffee
</div>
<div class="item">Go to work</div>
<div class="space"></div>
<div class="item doing-first">
<div class="bg-doing"></div>
1
</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="space"></div>
<div class="item done-first">
<div class="bg-done"></div>
1
</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="end"></div>
</div>
答案 7 :(得分:-2)
可能的JS解决方案..
.stop()