当 flexbox 项目以列模式换行时,容器不会增加其宽度

Posted

技术标签:

【中文标题】当 flexbox 项目以列模式换行时,容器不会增加其宽度【英文标题】:When flexbox items wrap in column mode, container does not grow its width 【发布时间】:2016-07-25 23:47:15 【问题描述】:

我正在研究一个嵌套的 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: wrapwrap-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 项目以列模式换行时,容器不会增加其宽度的主要内容,如果未能解决你的问题,请参考以下文章

当 flexbox 项目以列模式换行时,容器不会增加其宽度

CSS学习(二十一)-flexbox模型之二

Flex-basis不扩展宽度[重复]

Flexbox布局

CSS3 Flexbox属性可视化指南

flex布局模式简单概述