14

I have a flex container with flex-wrap: wrap and align-items: stretch so that children gets stretched when the container is resized up until the next child fits.

The problem is usually the last line has less elements than previous lines and children get stretched too much (all the boxes should look the same in every situation). How can I prevent that other than creating a bunch of empty boxes at the end of the container?

body {
    display: -webkit-flex;
    display: flex;
    -webkit-flex-wrap: wrap;
    flex-wrap: wrap;
    -webkit-align-items: stretch;
    align-items: stretch;
}
.child {
    height: 50px;
    margin: 10px;
    -webkit-flex: 1 1 50px;
    flex: 1 1 50px;
    background-color: red;
    border: 1px solid black;
}
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>

JSFiddle is here: http://jsfiddle.net/m_gol/B3wLG/2/

TylerH
  • 20,799
  • 66
  • 75
  • 101
mgol
  • 1,362
  • 15
  • 20
  • Other than *not* making the .child elements flexible, there's nothing you can really do. – cimmanon Aug 14 '13 at 17:49
  • 1
    related: http://stackoverflow.com/q/34928565/3597276 – Michael Benjamin Aug 22 '16 at 03:43
  • 2
    Also, `align-items: stretch` has nothing to do with this problem. The last row sizing is a *main axis* issue relating to `flex-grow: 1` on the flex items. [The `align-items` property works on the *cross axis*](http://stackoverflow.com/q/32551291/3597276). – Michael Benjamin Aug 22 '16 at 03:47

5 Answers5

5

The only solution I've found to this so far is to programatically add some empty child elements to make up a complete row - it's fine to add more than the row so they overspill (as they're empty).

Dominic
  • 62,658
  • 20
  • 139
  • 163
4

The dead easiest way is to remove the flex from the last item:

.flex-item:last-child{
  flex-grow: 0;
}

Or give it a small fraction of growth, so it doesn't look too little either (especially if everything else has grown a little)

.flex-item:last-child{
  flex-grow: 0.1;
}

If neither of these work, add an empty div as your last item, and give it a much larger growth - also hide any borders and backgrounds, and give it a zero height so it doesn't take up another line

.flex-item:last-child{
  background: none;
  border: none;
  height: 0px;
  flex-grow: 100;
}
Simo
  • 157
  • 1
  • 4
  • 1
    The first two code examples only work if you have just one item on the last row. If there are multiple items on that last row the ones before the last will grow to still fill up the row.The last one can also be achieved without having to add a dummy element by using :after on the container. See the first example in [my answer](https://stackoverflow.com/a/56848344/1871016). But this is probably still not what OP really needs. – Villermen Jul 02 '19 at 12:43
3

If you just want the last item to not stretch the solution can be kept simple by adding some css to the container:

body:after {
    content: "";
    flex: 99999 1 auto;
}

This prevents the items in the last row from stretching altogether, but in your case you probably want the last items to stretch as much as the previous rows. To accomplish that you'll have to add a lot of fake elements that size identically at the end of the container.

See this adjusted fiddle. You will need at least as many fake elements as you think the regular rows are going to contain. The items will start stretching more as soon as you run out of fake elements to fill the last row with.

bogus elements at end of container

Villermen
  • 815
  • 2
  • 13
  • 28
0

I think you would have to us JS to check if the width of the last qty N child elements match the width of the first element. For each element that doesn't match, unflex it (flex: none), then set it's width to match the width of the first element.

I put together a little script for it. Unfortunately, you have to add the class smoothFlex to the parent for it to work; or you can use some other selector.

I don't know a fast way to select all elements with a style display: flex.

Otherwise, the following script works exactly as I expect.

var RESIZE_DELAY = 120,
    FLEX_SELECTOR_STRING = '.smoothFlex',
    resizeTimeout = undefined;
$(window).on('resize.smoothFlex',function(){
    clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(function(){
        $(FLEX_SELECTOR_STRING).each(function(){
            var firstChildW = Math.floor($(this).children().first().width());
            $($(this).children(':gt(0)').get().reverse()).each(function(){
                if($(this).width()==firstChildW) return false;
                else $(this).css({
                    flex : "none",
                    width : firstChildW + "px",
                });
            })
        });
    }, RESIZE_DELAY);
});

IMO, the flexbox standard should have a native option for this - should be default if the parent is wrapped.

Sources:

Community
  • 1
  • 1
Femi
  • 1,332
  • 9
  • 20
0

Use percentages for your flex and then set a max and min width like so:

.child {
    height: 50px;
    margin: 10px;
    flex: 0 0 10%;
    min-width: 100px;
    max-width: 150px;
    background-color: red;
    border: 1px solid black;
}
Lee
  • 403
  • 1
  • 4
  • 16