ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在研究一个嵌套的 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 显示了我到目前为止所拥有的。

预期结果(我想要的):

https://i.stack.imgur.com/urybZ.png

实际结果(我得到的):

https://i.stack.imgur.com/YAVx2.png

较旧的结果(我在 2015 年得到的结果):

https://i.stack.imgur.com/t5SQd.png

更新

经过一番调查,这开始看起来像是一个更大的问题。所有主流浏览器的行为方式都相同,这与我的 flexbox 设计嵌套无关。更简单的 flexbox 列布局在项目换行时拒绝增加列表的宽度。

这个 other JSFiddle 清楚地说明了这个问题。在当前版本的 Chrome、Firefox 和 IE11 中,所有项目都正确换行;列表的高度在 row 模式下增加,但其宽度在 column 模式下不增加。此外,在改变 column 模式的高度时,根本不会立即重排元素,但在 row 模式中有。

但是,official specs (look specifically at example 5) 似乎表明我想做的事情应该是可能的。

有人可以想出解决此问题的方法吗?

更新 2

经过大量尝试使用 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 可以自动调整。如果列,宽度不能自动适应,是单个最大宽度。

M
Michael Benjamin

问题

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

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

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

很难将其视为错误,因为该问题发生在所有主要浏览器中。但它确实提出了一个问题:

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

你会认为其中至少有一个会做对。我只能推测原因。也许这是一个技术上难以实现的实现,并且在这次迭代中被搁置了。

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

问题说明

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

解决方法选项

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

“看来这个问题只用CSS是不能解决的,所以我建议你一个JQuery的解决方案。”

“奇怪的是,大多数浏览器都没有正确实现列弹性容器,但对写入模式的支持相当不错。因此,您可以使用垂直写入模式的行弹性容器。”

更多分析

铬错误报告

马克艾默里的回答

描述相同问题的其他帖子

Flex box 容器宽度没有增长

如何使 display:flex 容器水平扩展其包装的内容?

Flex-flow:列换行。如何设置容器的宽度等于内容?

Flexbox flex-flow 列在 chrome 中包装错误?

如何使用“flex-flow:列换行”?

内容换行时,Flex 容器不会展开

flex-flow:列换行,在 flex 框中导致父容器溢出

Html flexbox 容器不会在包裹的子项上展开

Flexbox 容器和溢出的 flex 子对象?

如何制作一个可以伸展以适应包裹物品的 flexbox 容器?

Flex容器计算一列,当有多列时

使用 flex 使容器全宽

Flexbox 容器可以调整大小吗?

Flex-Wrap 内部 Flex-Grow

Flexbox 增长到包含

将 flexbox 元素扩展到其内容?

flexbox 列拉伸以适应内容

https://stackoverflow.com/q/48406237/3597276

flex-flow:列换行不会拉伸父元素的宽度

为什么我的

    没有扩展宽度以覆盖所有
  • https://stackoverflow.com/q/55709208/3597276

    Flexbox wrap 不会增加父级的宽度?

    绝对 Flex 容器未更改为具有定义的最大高度的正确宽度


    根据错误报告评论,在他们发布新的布局引擎之前,这个问题不会得到解决LayoutNG
    您提供并对其进行分类的链接数量非常多。我希望大多数人都这样写答案。很好的答案。
    这是一个有用的存储库,列出了几个 Flexbox 错误和解决方法:This bug is listed at #14
    另一种解决方法:codepen.io/_mc2/pen/PoPmJRP 在某些方面,我发现写模式方法更好
    知道在 2020 年,前端仍然像 2000 年一样是一个 PITA,这有点奇怪地令人欣慰——只是有一组完全不同的错误。
M
MandeeD

聚会迟到了,但几年后仍然遇到这个问题。最终找到了使用网格的解决方案。在您可以使用的容器上

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;
我一直在思考 Flex,以至于我完全忘记了 Grids。这解决了问题!谢谢!!!!!
D
Dustin Kane

我刚刚在这里找到了一个非常棒的 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,它就会变成页面的宽度。足够好,就我的目的而言。
这是一个很好的解决方案!谢谢你帮了很多忙!!!!
作品!这个答案很难找到,值得更多的支持!
N
Neil Rackett

仅限 CSS 的解决方法

在提出这个问题将近 6 年后,这个 flexbox 错误 仍然 存在,所以这里有一个纯 CSS flex-direction: column 解决方法供其他任何人使用:

身体{背景颜色:灰色; } 按钮 { 背景颜色:白色;边框:无;边框半径:4px;宽度:80px;高度:40px;边距:4px; } /* flex-direction 的解决方法:带有 WRAP 的列在下面 */ .wrapped-columns { flex-direction: row;弹性包装:换行;写作模式:垂直lr;文本方向:直立; } /* 确保内容在 Firefox 中正确呈现 */ .wrapped-columns * { writing-mode: Horizontal-tb; }

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


太棒了,谢谢。这应该是公认的答案。
** 请注意,您必须将书写模式和方向更改回按钮中的内容才能在 Firefox 中工作
S
Steve Hanov

很遗憾,这么多主流浏览器多年后都出现了这个bug。考虑一个 Javascript 解决方法。每当浏览器窗口调整大小或将内容添加到元素时,执行此代码以使其大小调整为适当的宽度。你可以在你的框架中定义一个指令来为你做这件事。

    element.style.flexBasis = "auto";
    element.style.flexBasis = `${element.scrollWidth}px`;

I
Itay Gal

由于尚未提出解决方案或适当的解决方法,因此我设法通过稍微不同的方法获得了所请求的行为。我没有将布局分成 3 个不同的 div,而是将所有项目添加到 1 个 div 中,并在它们之间创建更多 div 的分隔。

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

将所有项目添加到使用 flex 包装项目的 1 个容器 div 每个“内部容器”的第一个项目(我将称之为部分)将有一个类,这有助于我们进行一些操作来创建分离和每个部分的样式。在每个第一个项目上使用 :before,我们可以找到每个部分的标题。使用空间会在部分之间创建间隙由于空间不会覆盖部分的整个高度,因此我还要添加 :after 到部分,因此将其定位为绝对位置和白色背景。为了设置每个部分的背景颜色,我在每个部分的第一项中添加了另一个 div。我也将处于绝对位置,并且将具有 z-index:-1。为了获得每个背景的正确宽度,我使用 JS,设置正确的宽度,并添加一个监听器来调整大小。

函数 calcWidth() { var size = $(document).width(); var end = $(".end").offset().left; var todoWidth = $(".doing-first").offset().left; $(".bg-todo").css("宽度", 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("宽度", doneWidth + 20); } 计算宽度(); $(window).resize(function() { calcWidth(); }); .container { 显示:弹性;弹性包装:换行;弹性方向:列;高度:120px;对齐内容:弹性开始;填充顶部:30px;溢出-x:自动;溢出-y:隐藏; } .item { 宽度:200px;背景颜色:#e5e5e5;边框半径:5px;高度:20px;边距:5px;位置:相对;盒子阴影:1px 1px 5px 0px rgba(0, 0, 0, 0.75);填充:5px; } .space { 高度:150px;宽度:10px;背景颜色:#fff;边距:10px; } .todo-first:before { 位置:绝对;顶部:-30 像素;高度:30px;内容:“待办事项(2)”;字体粗细:粗体; } .doing-first:before { 位置:绝对;顶部:-30 像素;高度:30px;内容:“做(5)”;字体粗细:粗体; } .doing-first:after, .done-first:after { position: absolute;顶部:-35px;左:-25px;宽度:10px;高度:180px; z 指数:10;背景颜色:#fff;内容: ””; } .done-first:before { 位置:绝对;顶部:-30 像素;高度:30px;内容:“完成(3)”;字体粗细:粗体; } .bg-todo { 位置:绝对;背景颜色:#FFEFD3;宽度:100vw;高度:150px;顶部:-30 像素;左:-10px; z 指数:-1; } .bg-doing { 位置:绝对;背景颜色:#EFDCFF;宽度:100vw;高度:150px;顶部:-30 像素;左:-15px; z 指数:-1; } .bg-done { 位置:绝对;背景颜色:#DCFFEE;宽度:10vw;高度:150px;顶部:-30 像素;左:-15px; z 指数:-1; } .end { 高度:150px;宽度:10px; }

喝咖啡
上班
< /div>
1
2
3
4
5
1
2
3< /div>


L
Lloyd Smith

我以这种方式解决了我的问题,我希望它可以帮助其他偶然发现的人:

  _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`;

}


y
yeahdixon

解决方法:

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

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

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

`

其中 refWrapper 是你的弹性元素


u
user2323336

可能的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