当 flexbox 项目以列模式换行时,容器不会增加其宽度
Posted
技术标签:
【中文标题】当 flexbox 项目以列模式换行时,容器不会增加其宽度【英文标题】:When flexbox items wrap in column mode, container does not grow its width 【发布时间】:2018-01-25 18:12:32 【问题描述】:我正在研究一个嵌套的 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) 似乎表明我想做的事情应该是可能的。
有人能想出解决这个问题的方法吗?
更新 2
在 resize 事件期间使用 javascript 更新各种元素的高度和宽度进行了大量实验后,我得出的结论是,尝试以这种方式解决它太复杂太麻烦了。此外,添加 JavaScript 肯定会破坏 flexbox 模型,应该尽可能保持干净。
现在,我回退到overflow-y: auto
而不是flex-wrap: wrap
,以便内部容器在需要时垂直滚动。它并不漂亮,但它是一种前进方式,至少不会过多地破坏可用性。
【问题讨论】:
只是一个旁注,但 Chrome 中存在一个错误,导致其流程设置为column wrap
的容器在包装时不会扩展其宽度。请参阅code.google.com/p/chromium/issues/detail?id=247963 了解更多信息。
我也无法让它在 IE11 中工作。在那里,最里面的项目会换行,但内部列表及其容器(外部项目)不会增加它们的宽度。目前我正在回退到让内部列表滚动,但这不是一个好的长期解决方案,我真的很想避免为此添加 JavaScript。
codepen.io/iugo/pen/ExWwjRY flex 和 wrap 时,如果是 row,height 可以自动调整。如果列,宽度不能自动适应,是单个最大宽度。
【参考方案1】:
问题
这看起来像是 flex 布局的一个根本缺陷。
列方向的弹性容器不会扩展以容纳额外的列。 (这在flex-direction: row
不是问题。)
这个问题已经被问过很多次了(见下面的列表),在 CSS 中没有明确的答案。
很难将此确定为错误,因为所有主要浏览器都会出现此问题。但它确实提出了一个问题:
怎么可能所有主流浏览器都拥有 flex 容器 在行方向上展开换行,但不在列方向上展开?
您会认为其中至少有一个会做对。我只能推测原因。也许这是一个技术上难以实现的实现,并且在这次迭代中被搁置了。
更新:该问题似乎在 Edge v16 中得到解决。
问题说明
OP 创建了一个有用的演示来说明问题。我在这里复制它:http://jsfiddle.net/nwccdwLw/1/
解决方法选项
来自 Stack Overflow 社区的 Hacky 解决方案:
"It seems this issue cannot be solved only with CSS, so I propose you a JQuery solution."
"It's curious that most browsers haven't implemented column flex containers correctly, but the support for writing modes is reasonably good. Therefore, you can use a row flex container with a vertical writing mode."
更多分析
Chromium Bug Report
Mark Amery's answer
描述相同问题的其他帖子
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://***.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://***.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【讨论】:
根据错误报告 cmets,直到他们发布新的布局引擎LayoutNG 您提供和分类的链接数量非常多。我希望大多数人都这样写答案。很好的答案。 这是一个有用的 repo,列出了几个 Flexbox 错误和解决方法:This bug is listed at #14 另一种解决方法:codepen.io/_mc2/pen/PoPmJRP 在某些方面我发现写模式方法更好 知道在 2020 年,前端仍然像 2000 年一样是一个 PITA —— 只是有一组完全不同的错误,这有点奇怪。【参考方案2】:聚会迟到了,但几年后仍然遇到这个问题。最终找到了使用网格的解决方案。在你可以使用的容器上
display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);
我有一个关于 CodePen 的示例,可以在 flexbox 问题和网格修复之间切换:https://codepen.io/MandeeD/pen/JVLdLd
【讨论】:
这是一个优雅的解决方法,如果我见过的话!我刚刚进入网格,我喜欢你的解决方案,谢谢你。 看准了!解决了我的问题。 在 Chrome 中仍然遇到这个问题。我希望避免使用网格解决方法。很高兴知道其他人正在使用这种方法! 救命稻草。如果您需要某些项目占用更多行,也可以使用grid-row-end: span X;
。
你成就了我的一天!谢谢!【参考方案3】:
我刚刚在这里找到了一个非常棒的 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。
【讨论】:
很棒的提示。我已经在 Firefox 中尝试过,它部分地在那里工作。在小提琴中,容器的红色背景只是其填充的宽度。如果我删除它的 display: inline-block,它就会变成页面的宽度。足够好,就我的目的而言。 这是一个很好的解决方案!谢谢你帮了很多忙!!!! 有效!这个答案很难找到,值得更多的支持!【参考方案4】:纯 CSS 解决方法
在提出这个问题将近 6 年后,这个 flexbox 错误仍然存在,所以这里有一个纯 CSS 的 flex-direction: column
解决方法供其他人使用:
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
。
【讨论】:
太棒了,谢谢。这应该是公认的答案。 ** 请注意,您必须将书写模式和方向更改回按钮中的内容才能在 Firefox 中使用【参考方案5】:很遗憾,这么多主流浏览器多年后都出现了这个错误。考虑一个 Javascript 解决方法。每当浏览器窗口调整大小或将内容添加到元素时,执行此代码以将其调整为适当的宽度。你可以在你的框架中定义一个指令来为你做这件事。
element.style.flexBasis = "auto";
element.style.flexBasis = `$element.scrollWidthpx`;
【讨论】:
【参考方案6】:由于尚未提出解决方案或适当的解决方法,我设法通过稍微不同的方法获得了所请求的行为。我没有将布局分成 3 个不同的 div,而是将所有项目添加到 1 个 div 中,并在它们之间创建更多 div 的分隔。
假设我们有 3 个部分,建议的解决方案是硬编码的,但可以扩展到一个通用部分。主要思想是解释我们如何实现这种布局。
-
将所有项目添加到 1 个使用 flex 包装项目的容器 div 中
每个“内部容器”的第一项(我将其称为部分)都有一个类,它可以帮助我们进行一些操作来创建每个部分的分隔和样式。
在每个第一项上使用
:before
,我们可以找到每个部分的标题。
使用space
会在各个部分之间创建间隙
由于space
不会覆盖该部分的全部高度,我还将:after
添加到这些部分中,以便将其定位为绝对位置和白色背景。
为了设置每个部分的背景颜色,我在每个部分的第一项内添加了另一个 div。我也将拥有绝对位置,并将拥有z-index: -1
。
为了获得每个背景的正确宽度,我使用的是 JS,设置了正确的宽度,还添加了一个监听器来调整大小。
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】:我以这种方式解决了我的问题,我希望它可以帮助其他偶然发现的人:
_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 = `$totalRealWidthpx`;
【讨论】:
【参考方案8】:解决方法:
使用 javascript 在屏幕上加载元素后手动设置包装器的宽度并不难。宽度始终是最后一个子元素的右手点。
在反应中,我会根据添加到 flex 包装器的任何子级更新布局更改,但这可以在您向包装器添加或删除子级的任何时候调用。
let r = refWrapper.current.lastElementChild.getBoundingClientRect()
refWrapper.current.style.width = (r.x+r.width )+'px'
`
refWrapper 是你的弹性元素
【讨论】:
【参考方案9】:可能的 JS 解决方案..
var ul = $("ul.ul-to-fix");
if(ul.find("li").length>max_possible_rows))
if(!ul.hasClass("width-calculated"))
ul.width(ul.find("li").eq(0).width()*ul.css("columns"));
ul.addClass("width-calculated");
【讨论】:
不幸的是,这无济于事,因为各个元素的高度可能会有所不同。所以元素的数量并不有趣......但是使用columns
样式属性可能是一个可行的解决方案的起点。也许调整列数和 ul的宽度。
我最终也为我一直在开发的 React 应用程序使用了一个 JS 解决方案来解决这个问题。它旨在与任何和不同大小的元素一起使用。此处示例:djskinner.github.io/react-column-wrap。源码在这里:github.com/djskinner/react-column-wrap以上是关于当 flexbox 项目以列模式换行时,容器不会增加其宽度的主要内容,如果未能解决你的问题,请参考以下文章