我正在做一个嵌套的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,以便内部容器在需要时垂直滚动。它并不漂亮,但至少是一种不太破坏可用性的方法。


当前回答

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

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

}

其他回答

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

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

}

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

由于还没有提出解决方案或适当的解决方案,我设法用不同的方法获得了所要求的行为。而不是将布局分离成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元素

我刚刚发现了一个非常棒的纯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。