我正在做一个嵌套的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列布局也拒绝在项目换行时增加列表的宽度。

另一个JSFiddle清楚地说明了这个问题。在当前版本的Chrome、Firefox和IE11中,所有项目都能正确换行;列表的高度在行模式下增加,但其宽度在列模式下不增加。同样,在改变列模式的高度时,根本没有元素的立即回流,但行模式中有。

然而,官方规格(具体看例5)似乎表明我想做的事情应该是可能的。

有人能想出解决这个问题的办法吗?

更新2

在多次尝试使用JavaScript在调整大小事件期间更新各种元素的高度和宽度后,我得出的结论是,尝试以这种方式解决它太复杂,太麻烦了。此外,添加JavaScript肯定会破坏flexbox模型,它应该尽可能保持干净。

现在,我回到了overflow-y: auto,而不是flex-wrap: wrap,以便内部容器在需要时垂直滚动。它并不漂亮,但至少是一种不太破坏可用性的方法。


当前回答

由于还没有提出解决方案或适当的解决方案,我设法用不同的方法获得了所要求的行为。而不是将布局分离成3个不同的div,我将所有的项目添加到1个div中,并在两者之间创建一些div的分离。

建议的解决方案是硬编码的,假设我们有3个部分,但可以扩展为一个通用的部分。主要的思想是解释我们如何实现这种布局。

Adding all the items into 1 container div that uses flex to wrap the items The first item of each "inner container" (I'll call it a section) will have a class, which helps us to do some manipulations that create the separation and styling of each section. Using :before on each first item, we can locate the title of each section. Using space creates the gap between the sections Since the space won't cover the full height of the section I'm also adding :after to the sections so positioning it with absolute position and white background. To style the background color of each section I'm adding another div inside the first item of each section. I will be position with absolute as well and will have z-index: -1. To get the correct width of each background, I'm using JS, setting the correct width, and also adding a listener to resize.

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>

其他回答

处理:

使用javascript在元素加载到屏幕上后手动设置包装器的宽度并不难。宽度始终是最后一个子元素的右手点。

在react中,我根据添加到flex包装器的任何子包装器的布局更改更新了它,但这可以在任何时候调用,你添加或删除子包装器。

let r = refWrapper.current.lastElementChild.getBoundingClientRect()
refWrapper.current.style.width = (r.x+r.width )+'px'

`

哪里refWrapper是你的你的flex元素

这个问题

这看起来像是flex布局的一个基本缺陷。

列方向的伸缩容器不会扩展以容纳其他列。(这在flex-direction: row中不是问题。)

这个问题已经被问过很多次了(见下面的列表),在CSS中没有明确的答案。

很难将此定位为一个bug,因为这个问题在所有主要浏览器中都会出现。但它确实提出了一个问题:

怎么可能所有主流浏览器都有flex容器呢 按行方向展开而不是按列方向展开?

你会认为他们中至少有一个人能答对。我只能推测原因。也许它是一个技术上困难的实现,在这次迭代中被搁置了。

更新:该问题似乎在Edge v16中得到解决。


问题说明

OP创建了一个有用的演示来说明这个问题。我复制到这里:http://jsfiddle.net/nwccdwLw/1/


方法选择

来自Stack Overflow社区的hack解决方案:

“看起来这个问题仅靠CSS无法解决,所以我建议您使用JQuery解决方案。” 奇怪的是,大多数浏览器都没有正确地实现列伸缩容器,但对写入模式的支持相当不错。因此,您可以使用具有垂直写入模式的行伸缩容器。”


更多的分析

铬Bug报告 Mark Amery的回答是


其他描述同样问题的帖子

Flex box container width doesn't grow How can I make a display:flex container expand horizontally with its wrapped contents? Flex-flow: column wrap. How to set container's width equal to content? Flexbox flex-flow column wrap bugs in chrome? How do I use "flex-flow: column wrap"? Flex container doesn't expand when contents wrap in a column flex-flow: column wrap, in a flex box causing overflow of parent container Html flexbox container does not expand over wrapped children Flexbox container and overflowing flex children? How can I make a flexbox container that stretches to fit wrapped items? Flex container calculating one column, when there are multiple columns Make container full width with flex Flexbox container resize possible? Flex-Wrap Inside Flex-Grow Flexbox grow to contain Expand flexbox element to its contents? flexbox column stretch to fit content https://stackoverflow.com/q/48406237/3597276 flex-flow: column wrap doesn't stretch the parent element's width Why doesn't my <ul> expand width to cover all the <li>? https://stackoverflow.com/q/55709208/3597276 Flexbox wrap not increasing the width of parent? Absolute Flex container not changing to the correct width with defined max-height

我刚刚发现了一个非常棒的纯CSS工作在这里。

https://jsfiddle.net/gcob492x/3/

难点:在列表div中设置写入模式:vertical-lr,然后在列表项中设置写入模式:horizontal-tb。我不得不调整JSFiddle中的样式(删除了很多对齐样式,这对解决方案来说是不必要的)。

注意:评论说它只适用于基于chrome的浏览器,而不是Firefox。我只在Chrome上亲自测试过。有可能有一种方法来修改它,使它在其他浏览器中工作,或者已经有更新到所说的浏览器,使这个工作。

当flexbox项目以列模式换行时,容器的宽度不会增长。通过挖掘这个问题线程,我找到了https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39,这让我找到了这个JSFiddle。

由于还没有提出解决方案或适当的解决方案,我设法用不同的方法获得了所要求的行为。而不是将布局分离成3个不同的div,我将所有的项目添加到1个div中,并在两者之间创建一些div的分离。

建议的解决方案是硬编码的,假设我们有3个部分,但可以扩展为一个通用的部分。主要的思想是解释我们如何实现这种布局。

Adding all the items into 1 container div that uses flex to wrap the items The first item of each "inner container" (I'll call it a section) will have a class, which helps us to do some manipulations that create the separation and styling of each section. Using :before on each first item, we can locate the title of each section. Using space creates the gap between the sections Since the space won't cover the full height of the section I'm also adding :after to the sections so positioning it with absolute position and white background. To style the background color of each section I'm adding another div inside the first item of each section. I will be position with absolute as well and will have z-index: -1. To get the correct width of each background, I'm using JS, setting the correct width, and also adding a listener to resize.

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>

CSS-only解决方案

在这个问题提出近6年后,这个flexbox漏洞仍然存在,所以这里有一个css只适用于flex direction:列的解决方案,以解决其他任何最终出现在这里的问题:

body { background-color: grey; } button { background-color: white; border: none; border-radius: 4px; width: 80px; height: 40px; margin: 4px; } /* WORKAROUND FOR flex-direction: column WITH WRAP IS BELOW */ .wrapped-columns { flex-direction: row; flex-wrap: wrap; writing-mode: vertical-lr; text-orientation: upright; } /* Ensures content is rendered correctly in Firefox */ .wrapped-columns * { writing-mode: horizontal-tb; } <div class="wrapped-columns"> <button>Button 1</button> <button>Button 2</button> <button>Button 3</button> <button>Button 4</button> <button>Button 5</button> <button>Button 6</button> <button>Button 7</button> <button>Button 8</button> <button>Button 9</button> <button>Button 10</button> <button>Button 11</button> <button>Button 12</button> <button>Button 13</button> <button>Button 14</button> </div>

此解决方案提供了与flex-direction: column相同的结果,并且适用于flex-wrap: wrap和wrap-reverse。