24

As you can see from the Snippet below (View as Fiddle), an absolutely positioned, columnar flexbox won't expand to fit its children.

In Chrome, for example, it will only be as wide as the widest child element and as tall as the shortest column.

Can anyone suggest a solution without using any additional markup?

Edit: The number of items in the list will be dynamic, as will the text in each item. I need to break to a new column after a set number of items.

*{box-sizing:border-box;}
ul{
    background:#090;
    display:flex;
    flex-flow:column wrap;
    left:0;
    list-style:none;
    max-height:202px;
    padding:5px;
    position:absolute;
    top:0;
}
li{
    background:rgba(255,0,0,.75);
    color:#fff;
    flex:1 0 30px;
    font-family:arial;
    line-height:30px;
    max-height:30px;
    margin:1px;
    padding:0 2px;
    min-width:100px;
}
<ul>
    <li>Line 0</li>
    <li>Line 1</li>
    <li>Line 2</li>
    <li>Line 3</li>
    <li>Line 4</li>
    <li>Line 5</li>
    <li>Line 6</li>
    <li>Line 7</li>
    <li>Line 8 is a little bit longer</li>
</ul>
Shaggy
  • 6,696
  • 2
  • 25
  • 45
  • Do you need the data to be in two columns? – lharby Apr 09 '15 at 10:37
  • The number of items in the list will be dynamic, I'm trying to get it to break into a new column after a set number so, potentially, there could be any number of columns. – Shaggy Apr 09 '15 at 10:46
  • You might have to use percentages for that then. – lharby Apr 09 '15 at 10:46
  • Can you elaborate on that, lharby? – Shaggy Apr 09 '15 at 10:50
  • Sorry, I don't know a massive amount about the flex properties. I tried setting it as the default attribute for clearfix with a fallback for display:block, so older browsers will interpret it. I was thinking you could float the li's and then give them width's of 50%, 33%, 25% etc, but I think that defeats the purpose of using the flex property. – lharby Apr 09 '15 at 14:29
  • The clearfix class should wrap the content of the child elements regardless of their width or height even with absolute positioning. – lharby Apr 09 '15 at 14:30
  • Why don't you want to use additional markup? – sergdenisov May 13 '15 at 18:52
  • The problem you encounter is not related to absolute positioning, it is a flaw in Flexbox. As you can see in this fiddle ... https://jsfiddle.net/tctfav4c/ ... if to change to `inline-flex`, it still behaves the same. I closed this as a duplicate, and let me know if you think that was not okay. – Asons Aug 14 '17 at 21:03
  • @LGSon, isn't the norm to close the newer question as a dupe of the older? – Shaggy Oct 25 '17 at 23:23
  • @Shaggy I don't know if there is a norm, though that is often the case, but if an answer at a newer question is more correct, better written, or the question is better presented, it happens more and more the old question gets closed as a duplicate, to promote quality content in favor of being the first...and in this case it is the answer given, which better describes the issue – Asons Oct 25 '17 at 23:38

2 Answers2

10

A flex box with the position:absolute; is no longer considered a flex box hence your current issue.

You must use predefined widths and heights if your going to use position:absolute;.


See here for w3c on flexboxes http://www.w3.org/TR/css3-flexbox/#flex-items

"flex items themselves are flex-level boxes, not block-level boxes: they participate in their container’s flex formatting context, not in a block formatting context."

By changing to position:absolute; you force the flex container to be a block element (this happens to all elements with position:absolute;), thus eliminating the flexible nature of it and also because its contents are not block elements they are flex children they can only effect a flex level container not a block one .


The Jquery Solution

Right so there is a way to do it with jquery.

This might not be what you wanted because it's not CSS only but it does work.

What I'am doing here is getting the width of the container at its original width.

Then getting the max width of every 6th element (because that's when your row's break to form a new column).

Then counting the total number columns there is and use that to multiply the max width of each column by the number of column's and then finally add it all together to get the desired width of your container.

feel free to take away and add new column's to test it all out.

Fiddle

var count = $("ul.flex-container li:nth-child(6n)").length;

var firstWidth = Math.max.apply(null, $("ul.flex-container").map(function() {
  return $(this).outerWidth(true);
}).get());

var childWidth = Math.max.apply(null, $("ul.flex-container li:nth-child(6n+6)").map(function() {
  return $(this).outerWidth(true);
}).get());

var totalWidth = childWidth * count

$("ul.flex-container").css({
  width: firstWidth + totalWidth,
});
ul.flex-container {
  background: #090;
  display: flex;
  flex-flow: column wrap;
  list-style: none;
  max-height: 192px;
  padding: 5px;
  position: absolute;
}
ul.flex-container li {
  background: rgba(255, 0, 0, .75);
  color: #fff;
  flex: 1 0 30px;
  font-family: arial;
  line-height: 30px;
  margin: 1px;
  padding: 0 2px;
  min-width: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul class="flex-container">
  <li>Line 0</li>
  <li>Line 1</li>
  <li>Line 2</li>
  <li>Line 3</li>
  <li>Line 4</li>
  <li>Line 5</li>
  <li>Line 6</li>
  <li>Line 7</li>
  <li>Line 8 is a little bit longer</li>
  <li>Line 0</li>
  <li>Line 1</li>
  <li>Line 2</li>
  <li>Line 3</li>
  <li>Line 4</li>
  <li>Line 5</li>
  <li>Line 6</li>
  <li>Line 7</li>
  <li>Line 8 is a little bit longer</li>
</ul>
  • Thanks, DCdaz. I had another read of that document and while it states that "An absolutely-positioned **child** of a flex container does not participate in flex layout", I see no mention of an absolutely positioned flex container. – Shaggy May 12 '15 at 16:00
  • HI Shaggy probs best just having a read through it to get the gist of it this is a key area. *"flex items themselves are flex-level boxes, not block-level boxes: they participate in their container’s flex formatting context, not in a block formatting context."* by changing to absolute you force the flex container to be a block element thus eliminating the flexible nature of it and in turn its contents are not block elements there are flex children that can only effect a flex level container not a block one. –  May 13 '15 at 08:26
  • You should add that to your answer :) – Shaggy May 13 '15 at 16:30
  • Upvoted for now. If nobody comes up with a solution in the next day or 2, I'll accept this as the answer as it explains why the problem occurs. I don't know, though, if I'll be able to award the bounty as your answer doesn't include a solution which is what I'm really after. – Shaggy May 14 '15 at 09:15
  • There we go a completely working version of what you want though it does use jquery. –  May 14 '15 at 12:19
  • I'm going to accept this for now, as it provides a working solution to the problem presented. Unfortunately, as it doesn't provide the solution I was looking for, I'm afraid I can't award you the bounty. – Shaggy May 18 '15 at 10:04
0

The reason your child elements won't fit in the flex-container is that you set a max-height on container .

But what is happening in your original sample is strange (specially the child elements getting unstacked from column );

it looks like the flex-items are not being able to recognize the container borders while the flexbox is trying to stuff them in anyway (even changing flex-direction:column);
the only thing that actually makes sense is the maximum width of the widest element.

here's a snippet without max-width:

ul{
    background:#090;
    display:flex;
    flex-flow:column wrap;
    list-style:none;
    padding:5px;
    position:absolute; /*max-height:192px;*/
}
/*ul::after{display:table;clear:both; content:''}clearfix*/
li{
    background:rgba(255,0,0,.75);
    color:#fff;
    flex:1 0 30px;
    font-family:arial;
    line-height:30px;
    margin:1px;
    padding:0 2px;
    min-width:100px;
}
<ul>
    <li>Line 0</li>
    <li>Line 1</li>
    <li>Line 2</li>
    <li>Line 3</li>
    <li>Line 4</li>
    <li>Line 5</li>
    <li>Line 6</li>
    <li>Line 7</li>
    <li>Line 8 is a little bit longer</li>
</ul>

EDIT

I don't know what your desired output is but a typical flexbox implementation could be something like this:

ul{
    background:#090;
    display:flex;
    flex-flow:column wrap;
    list-style:none;
    padding:5px;
    /*position:absolute;*/
    height:192px;
}
   /*ul::after{display:table;clear:both; content:''}/*clearfix*/
li{
    background:rgba(255,0,0,.75);
    color:#fff;
    flex:1 0 30px;
    font-family:arial;
    line-height:30px;
    margin:1px;
    padding:0 2px;
   /* width:100px;*/
}
<ul>
    <li>Line 0</li>
    <li>Line 1</li>
    <li>Line 2</li>
    <li>Line 3</li>
    <li>Line 4</li>
    <li>Line 5</li>
    <li>Line 6</li>
    <li>Line 7</li>
    <li>Line 8 is a little bit longer</li>
</ul>
maioman
  • 18,154
  • 4
  • 36
  • 42
  • This doesn't answer my question in any way, it just, essentially, repeats what I said. – Shaggy May 13 '15 at 16:31
  • doesn't deleting `max-height` solve your problem? the snippet seems ok – maioman May 13 '15 at 20:38
  • No, it doesn't. I want to break to a new column when that `max-height` is reached. – Shaggy May 13 '15 at 20:40
  • `flex-flow` property is a shorthand for `flex-direction` + `flex-wrap` , so in your case flex-direction's value is `column` , the expected behavior is that it expands vertically, and max-height is breaking the flexbox – maioman May 13 '15 at 20:46
  • It expands vertically and wraps if necessary/possible, hence the `flex-wrap` property. The `max-height` property is not the issue here, it's what is causing the child elements to be laid out exactly as I want them. The issue is with the parent not behaving as expected. – Shaggy May 13 '15 at 23:18