Make Flex container width 'shrink wrap' to content, up to a certain width?

Viewed 202

I want each row of the flex container to fit as many flex elements of random width as possible, up to a certain maximum container width, and the flex container width must shrink to the width of the widest resulting row.

When I try to create a flex container with a certain maximum width, there is always some surplus space between the widest row and the edge(s) of the container.

I have searched for hours but cannot find a solution.

Example:

.photos {
    max-width: 500px;
    margin: 0px auto 0px auto;
    box-shadow: 0px 1px 3px rgba(0,0,0,0.16);
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
  border: 1px solid black;
}

.photo {
    height: 50px;
  border: 1px solid red;
}
<!DOCTYPE html>
<head>
    <link rel="stylesheet" href="flex_container.css">
</head>
<body>
     <div class="photos">
          <div class="photo" style="width: 155px"></div>
          <div class="photo" style="width: 254px"></div>
          <div class="photo" style="width: 233px"></div>
          <div class="photo" style="width: 387px"></div>
     </div>
</body>

1 Answers

If you have access to the sizes of the photos before hand or can get them, you can create an array of the widths and then loop over the list in order and find which row will be the largest less than max. Once found you can set the width style value to this new width.

<html>
<head>
    <style>
        .photos {
            margin: 0px auto 0px auto;
            box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.16);
            max-width:500px;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            border: 1px solid black;
        }

        .photo {
            height: 50px;
            border: 1px solid red;
        }
    </style>
</head>
<body>
    <div id="photos" class="photos">
        <div class="photo" style="width: 155px"></div>
        <div class="photo" style="width: 254px"></div>
        <div class="photo" style="width: 233px"></div>
        <div class="photo" style="width: 387px"></div>
    </div>
</body>
<script>
    var photos = [155, 254, 233, 387];
    var actualWidth = 0;
    var maxWidth = 500;
    var currentWidth = 0;
    var columnCount = 0;
    var actualColumnCount = 0;

    // get div that will need its width adjusted
    var container = document.getElementById('photos');
    // loop through each of the photos widths
    photos.forEach((pWidth) => {
        // is our combined width more than the max width
        if (pWidth + currentWidth > maxWidth) {
            // is the currentWidth more than the actualWidth
            console.log(currentWidth);
            actualWidth = checkActual(currentWidth, actualWidth);
            // with border css you are adding 2*width to the width of the box model. so we need to accoutn for this
            actualColumnCount = checkActual(columnCount, actualColumnCount);
            // reset the currentWidth as we are now starting a new row
            currentWidth = pWidth;
            // same as above
            columnCount = 1;
        } else {
            // add currentWidth to loopvalue width
            currentWidth += pWidth;
            // increment columnCount
            columnCount += 1;
        }
    });
    console.log(currentWidth);
    // one last check incase last element would make the highest row count
    actualWidth = checkActual(currentWidth, actualWidth);
    actualColumnCount = checkActual(columnCount, actualColumnCount);
    // set the width the largest width for a row and add the offset to account for the border
    container.style.width = actualWidth + 2 * actualColumnCount;

    // since both sets of compared values are numbers, we can use this generic function for our test
    function checkActual(current, actual) {
        return current > actual ? current : actual;
    }
</script>
Related