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

vu8f3i0k  于 2023-05-19  发布在  其他
关注(0)|答案(9)|浏览(120)

我正在使用一个嵌套的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模式中会有。
然而,官方规格 (具体看示例5) 似乎表明我想做的事情应该是可能的。
有人能想出一个解决这个问题的方法吗?

更新2

在使用JavaScript在调整大小事件期间更新各种元素的高度和宽度的大量实验之后,我得出的结论是,试图以这种方式解决它太复杂,太麻烦了。此外,添加JavaScript肯定会破坏flexbox模型,应该尽可能保持干净。
现在,我将使用overflow-y: auto而不是flex-wrap: wrap,以便在需要时垂直滚动内部容器。它并不漂亮,但它是一种前进的方式,至少不会破坏可用性太多。

lrpiutwd

lrpiutwd1#

问题
这看起来像是flex布局中的一个基本缺陷。
列方向的Flex容器不会扩展以容纳其他列。(这在flex-direction: row中不是问题。)
这个问题已经被问了很多次(见下面的列表),在CSS中没有明确的答案。
很难将其视为bug,因为该问题在所有主流浏览器中都会发生。但它确实提出了一个问题:
为什么所有主流浏览器都能让flex容器在换行时按行方向扩展,而不是按列方向扩展?
你会认为他们中至少有一个会做对。我只能推测原因。也许这是一个技术上很困难的实现,因此在这次迭代中被搁置了。

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

问题说明

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

解决方案

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

更多分析

其他描述相同问题的帖子

lawou6xi

lawou6xi2#

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

display: grid;
grid-auto-flow: column;
grid-template-rows: repeat(6, auto);

我在CodePen上有一个例子,可以在flexbox问题和网格修复之间切换:https://codepen.io/MandeeD/pen/JVLdLd

mbzjlibv

mbzjlibv3#

纯CSS解决方案

在这个问题被提出近6年后,这个flexbox bug * 仍然存在,所以这里有一个仅限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

6xfqseft

6xfqseft4#

我刚刚在这里找到了一个非常棒的纯CSS解决方案。
https://jsfiddle.net/gcob492x/3/
技巧:在列表div中设置writing-mode: vertical-lr,然后在列表项中设置writing-mode: horizontal-tb。我不得不调整JSFiddle中的样式(删除了许多对齐样式,这对于解决方案来说是不必要的)。
注意:评论说它只适用于基于Chromium的浏览器,而不是Firefox。我只在Chrome上测试过。有可能有一种方法可以修改它,使其在其他浏览器中工作,或者有更新说,浏览器,使这一工作。
Big Shoutout对此评论:当flexbox项以列模式换行时,容器不会增加其宽度。通过挖掘这个问题线索,我找到了https://bugs.chromium.org/p/chromium/issues/detail?id=507397#c39,这让我找到了这个JSFiddle。

nnsrf1az

nnsrf1az5#

不幸的是,许多主要的浏览器在多年后都遭受了这个bug的困扰。考虑一个Javascript解决方案。每当浏览器窗口调整大小或向元素添加内容时,执行此代码以使其调整大小为适当的宽度。你可以在你的框架中定义一个指令来为你做这件事。

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

mi7gmzs66#

由于还没有建议解决方案或适当的变通方法,我设法用一种稍微不同的方法获得了所请求的行为。我没有把布局分成3个不同的div,而是把所有的项目都添加到1个div中,然后在中间再加上一些div。
建议的解决方案是硬编码的,假设我们有3个部分,但可以扩展到一个通用的。主要思想是解释我们如何实现这种布局。
1.将所有项添加到1个容器div中,该容器div使用flex Package 项
1.每个“内部容器”的第一项(我称之为节)将有一个类,它帮助我们进行一些操作,创建每个节的分隔和样式。
1.在每个第一项上使用:before,我们可以找到每个部分的标题。
1.使用space可在各部分之间创建差距
1.由于space不会覆盖整个部分的高度,我还将:after添加到部分中,以便使用绝对位置和白色背景来定位它。
1.为了设置每个部分的背景颜色,我在每个部分的第一个项目中添加了另一个div。我将位置与绝对以及将有z-index: -1
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>
ycl3bljg

ycl3bljg7#

我用这种方式解决了我的问题,我希望它能帮助其他在这个问题上绊倒的人:

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

}

qoefvg9y

qoefvg9y8#

解决方法:
使用javascript在元素加载到屏幕上之后手动设置 Package 器的宽度并不难。宽度将始终是最后一个子元素的右手点。
在react中,我让它根据添加到flex Package 器的任何子元素的布局变化进行更新,但这可以在您向 Package 器添加或删除子元素的任何时候调用。

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


其中refWrapper是您的flex元素

0pizxfdo

0pizxfdo9#

可能的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");
     }
}

相关问题