Shrink images to fit in flexbox with wrapping rows - html

I want an arbitrary number of images to shrink to fit inside a fixed-width div.
It seems that flexbox is good for this purpose, but I can't seem to get it to work with wrapping multiple rows.
If I set flex-flow to row wrap and flex to 1 1 auto, the images are too big.
.container {
display: flex;
flex-flow: row wrap;
border: 1px solid black;
width: 300px;
height: 300px;
}
.image {
flex: 1 1 auto;
}
img {
max-width: 100%;
}
<div class="container">
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
</div>
If I set flex-flow to just row or flex to 1 1 0, the images don't wrap onto multiple lines:
.container {
display: flex;
flex-flow: row wrap;
border: 1px solid black;
width: 300px;
height: 300px;
}
.image {
flex: 1 1 0;
}
img {
max-width: 100%;
}
<div class="container">
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
</div>
How can I make the images expand enough to wrap as much as possible to fill the div, but not so much they break out of it?

If I set flex-flow to row wrap and flex to 1 1 auto, the images are too big.
With flex-basis: auto your item will take the size of its content (the image, in this case). That's what you're getting in your first demo: The intrinsic width of the image.
If I set flex-flow to just row or flex to 1 1 0, the images don't wrap onto multiple lines.
With flex-basis: 0 your item will ignore content size and distribute all space in the row evenly among items. That's what's happening in your second demo: It's creating three equal width items. As a result, there is no reason to wrap.
To control the size of flex items you need to define their flex-basis. Here's a rough sketch:
.container {
display: flex;
flex-flow: row wrap;
align-content: flex-start; /* pack wrapping lines to the top */
border: 1px solid black;
width: 300px;
height: 300px;
}
.image {
flex: 0 0 30%;
margin: 5px;
}
img {
max-width: 100%;
}
<div class="container">
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
<div class="image">
<img src="http://www.placebacon.net/400/300">
</div>
</div>

Just for fun.
var container1 = document.querySelector('.container1'),
container2 = document.querySelector('.container2'),
img = container2.querySelectorAll('.container2 img'),
width = 50,
stop = false;
expand();
function expand() {
for (var i = 0; i < img.length; i++) {
img[i].style.width = width + 'px';
}
if (stop) return;
if (container2.clientHeight < container1.clientHeight) {
width++;
setTimeout(function() {
expand()
}, 10);
} else {
width--;
stop = true;
expand();
}
}
.container1 {
border: 1px solid black;
height: 300px;
width: 300px;
}
.container2 {
font-size: 0;
}
<div class="container1">
<div class="container2">
<img src="http://www.placebacon.net/400/300">
<img src="http://www.placebacon.net/400/300">
<img src="http://www.placebacon.net/400/300">
</div>
</div>

Related

Resize flex items to fit container (prevent items from overflowing)

I've been struggling with an issue that i think will have a pretty simple solution, but i just can't see it right now. I have a nested flexbox layout. The first level of flex items are divs that are display:flex themselves, and depending on class they can have a flex-directon: of row or column. Both the first and second level of these nested flex items should grow to fill up the remaining parent size, and shrink if new elements are added, never overflowing.
My Problem: When adding new flex items to the "first level", this seems to work fine. However, adding new flex items to the second level seems to always overflow the parent container height.
As i'm not really good at describing stuff like this, here's a picture of what i need:
The purple box is the parent container (not flex)
The orange box is the flex container with flex-direction: row, with the red lines being its flex items (each item is a flexbox as well).
The blue lines are the nested flex items.
Nested Flexbox layout
Adding new red items works and expands or shrinks as needed, but adding new blue items, overflows the container.
https://jsfiddle.net/t40x7or8/
Here's my code sample:
CSS
width: 1800px;
height: 500px;
border: 4px blue solid;
}
.flex-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.flex-item {
flex-grow: 1;
flex-basis: 0;
display: flex;
}
.flex-item > div {
flex-grow: 1;
border: 1px solid orange;
}
.height-1 {
flex-direction: column;
max-height: 100%;
}
.height-2 {
max-height: 100%;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
HTML
<div class="flex-container">
<div class="height-2 flex-item">
<div class="img-div">
<img src="https://picsum.photos/1080/600" />
</div>
</div>
<div class="height-1 flex-item">
<div class="img-div">
<img src="https://picsum.photos/1080/601" />
</div>
<div class="img-div">
<img src="https://picsum.photos/1080/602" />
</div>
</div>
</div>
</div>
Sorry for the chaotic description, i'm quite bad at these kind of things.
Thanks for the help
I think we should use flex: 1 1 0 for the fix the layout of the flex items.
And using width: 100% to control the size of the inner image.
https://jsfiddle.net/ramseyfeng/kxf46bju/
.page {
border: 3px solid green;
width: 600px;
height: 300px;
}
.flex {
display: flex;
}
.cols {
display: flex;
flex-direction: row;
}
.rows {
display: flex;
flex-direction: column;
}
.flex-1-1-0 {
flex: 1 1 0;
}
.img-div {
flex: 1 1 0;
display: flex;
}
.img-div img {
width: 100%;
}
<div class="page cols">
<div class="flex-1-1-0 rows">
<div class="img-div">
<img src="https://picsum.photos/1080/600" />
</div>
</div>
<div class="flex-1-1-0 rows">
<div class="img-div">
<img src="https://picsum.photos/1080/601" />
</div>
<div class="img-div">
<img src="https://picsum.photos/1080/602" />
</div>
</div>
<div class="flex-1-1-0 rows">
<div class="img-div">
<img src="https://picsum.photos/1080/607" />
</div>
<div class="img-div">
<img src="https://picsum.photos/1080/608" />
</div>
</div>
<div class="flex-1-1-0 rows">
<div class="img-div">
<img src="https://picsum.photos/1080/605" />
</div>
<div class="img-div">
<img src="https://picsum.photos/2000/1000" />
</div>
<div class="img-div">
<img src="https://picsum.photos/2000/1001" />
</div>
</div>
</div>

Scale Image Height to Flex-Grown Wrapper

I'm doing some flex-grid and having problems getting inline images to scale their dimensions to match the height of a flex-grown box.
I have a flexbox column where I have a title and a wrapper that will grow to whatever space is leftover. Inside the wrapper is list of images that I would like to all have the same height and be inline. No matter what I do the images end up to the right of the title and outside the dimensions of the flexbox. Any help would be appreciated. Bad drawing included on what I would like vs what I currently get
Link to fiddle: https://jsfiddle.net/oan5fmtb/1/
<div class="container">
<h4>Title</h4>
<div class="imgs">
<img src="..." class="img">
<img src="..." class="img">
<img src="..." class="img">
<img src="..." class="img">
...
</div>
</div>
.container {
height: 170px;
width: 100%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.imgs {
flex: 1;
/* Not sure if I need anything else here */
}
.img {
display: inline;
/* Not sure what to do */
}
Please try this,
.container {height: 170px;width: 100%;border: 1px solid black;}
.imgs {display: flex;}
.img{width:60px;height:60px;margin-right: 10px;}
It works as expected
Found the solution thanks to this answer.
<div class="container">
<h4>Title</h4>
<div class="imgs">
<img src="..." class="img">
<img src="..." class="img">
</div>
</div>
.container {
height: 170px;
width: 100%;
display: flex;
flex-direction: column;
border: 1px solid black;
}
.imgs {
flex: 1;
min-height: 0;
border: 1px solid red;
}
.img {
height: 100%;
width: auto;
}
I found out that a flex item cannot shrink smaller than the size of their content, min-height: 0 will fix this.

Cropping to the Shortest Image Height in a Single Row of Images?

Is it possible to crop images to the shortest height of an image in a single row of images? If so, can this be done solely with CSS?
Here is an example of what I'd like to accomplish:
And here is the code I am currently working on:
.container {
width: 100%;
margin: 0 auto;
}
.row {
width: 100%;
background: black;
display: flex;
align-items: center;
overflow: auto;
}
.row .item {
width: 33.33333%;
}
.row img {
display: block;
object-fit: cover;
width: 100%;
}
<div class="container">
<div class="row">
<div class="item">
<img src="https://78.media.tumblr.com/708bb6dcdaf359fd2ea83d11a0b5b4b8/tumblr_oyslstg5xk1unhdoco3_r1_1280.jpg" />
</div>
<div class="item">
<img src="https://78.media.tumblr.com/c49ef0b40483c35ad3b6898c0e037e11/tumblr_oyslstg5xk1unhdoco2_r1_1280.jpg" />
</div>
<div class="item">
<img src="https://78.media.tumblr.com/737456a69c07da1a9a2784506f62dce9/tumblr_oyslstg5xk1unhdoco9_r1_1280.jpg" />
</div>
</div>
</div>
Any solutions or advice?
It's probably best to use JS for this, unless you can process the heights on a backend script and set a height on the container as it is output.
Anyways, here is a little bit of JS that should achieve what you want:
const items = [...document.querySelectorAll('.item')];
const row = document.querySelector('.row');
function normalizeImages(images, container) {
const shortestItem = images.reduce((smallest, element) => {
return element.clientHeight < smallest.clientHeight ? element : smallest;
});
container.style.height = `${shortestItem.clientHeight}px`;
}
normalizeImages(items, row);
window.addEventListener('resize', () => normalizeImages(items, row));
.container {
width: 100%;
margin: 0 auto;
}
.row {
width: 100%;
background: black;
display: flex;
align-items: center;
overflow: hidden;
}
.row .item {
flex-basis: 33.33333%;
}
.row img {
display: block;
width: 100%;
}
<div class="container">
<div class="row">
<div class="item">
<img src="https://78.media.tumblr.com/708bb6dcdaf359fd2ea83d11a0b5b4b8/tumblr_oyslstg5xk1unhdoco3_r1_1280.jpg" />
</div>
<div class="item">
<img src="https://78.media.tumblr.com/c49ef0b40483c35ad3b6898c0e037e11/tumblr_oyslstg5xk1unhdoco2_r1_1280.jpg" />
</div>
<div class="item">
<img src="https://78.media.tumblr.com/737456a69c07da1a9a2784506f62dce9/tumblr_oyslstg5xk1unhdoco9_r1_1280.jpg" />
</div>
</div>
</div>
Note that a small amount of CSS has changed to hide overflow on the .row element

Aligning Images with Flexbox

I'm trying to have 1 image on the top with 2 images below it spanning half the width of the image on top. The problem I'm encountering are aligning issues with the 2 images in which they don't fit nicely below the top image but spaced perfectly to the sides. I'm trying to have the 2 images be below the top image perfectly aligned in the center with no spaces below. As I shrink the window, that is when it looks like I want it to on a full screen except without the spaces. I also want them perfectly aligned in the center of the screen.
https://jsfiddle.net/voryuja8/
.first {
display: flex;
}
.first img {
margin: auto;
max-width: 800px;
}
.second {
display: flex;
}
.second img {
margin: auto;
flex: 1;
max-width: 400px;
}
<div class="first">
<img src="https://sarahannephoto.files.wordpress.com/2015/01/devyn_015.jpg?w=1008" alt="">
</div>
<div class="second">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
</div>
Just remove margin:auto from the images and use justify-content: center in the flex containers.
.first {
display: flex;
justify-content: center;
}
.first img {
max-width: 800px;
}
.second {
display: flex;
justify-content: center;
}
.second img {
flex: 1;
max-width: 400px;
}
<div class="first">
<img src="https://sarahannephoto.files.wordpress.com/2015/01/devyn_015.jpg?w=1008" alt="">
</div>
<div class="second">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
</div>
This might be a bit better, as it doesn't rely on setting pixel values for anything: https://jsfiddle.net/voryuja8/1/
HTML:
<div class="second">
<div class="flex-child">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
</div>
<div class="flex-child">
<img src="http://preen.inquirer.net/files/2016/05/preen-emily-oberg-complex-e1463660455785.jpg" alt="">
</div>
</div>
CSS:
img {
max-width: 100%;
}
.second {
display: flex;
}
.flex-child {
flex-grow: 1;
flex-shrink: 1;
}

How to use `flex: grow` on floating content?

I have a header with 2 rows of 2 Foundation columns of content, as below:
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
HEADER
</div>
<div class="large-6 columns">
menu
</div>
</div>
<div class="row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
The .header height is dynamic and not set. I want the .image element to take up 100% of the remaining vertical space.
eg:
To that affect I have tried using flex and flex-grow, eg:
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
}
.image-container {
flex-grow: 1;
}
but had no luck, see fiddle: https://jsfiddle.net/9kkb2bxu/46/
Would anyone know how I could negate the dynamic height of the header from the 100vh of the image container?
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
border: 1px solid black;
background-color: #ccc;
}
.row {
width: 100%;
}
.header {
background-color: green;
}
.info {
background-color: yellow;
border: 1px solid #ccc;
}
.image-container {
border: 1px solid black;
display: flex;
}
.image {
background-color: red;
flex-grow: 1;
width: 100%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet"/>
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
<h1>
HEADER
</h1>
</div>
<div class="large-6 columns">
<h1>
menu
</h1>
</div>
</div>
<div class="row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
Set the second row to take up the rest of the remaining height with flex: 1 and make sure you nest that flex with display: flex:
.row.target-row {
flex: 1;
display: flex;
}
Set the .image-container to 100% height of its column parent.
.image-container {
height: 100%;
}
By default both columns will expand. Stop the left column from expanding with:
.large-5 {
align-self: flex-start;
}
(flex-start reference: https://stackoverflow.com/a/40156422/2930477)
Complete Example
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
border: 1px solid black;
background-color: #ccc;
}
.row {
width: 100%;
}
.header {
background-color: green;
}
.info {
background-color: yellow;
border: 1px solid #ccc;
}
.image-container {
height: 100%;
background-color: red;
}
.large-5 {
align-self: flex-start;
}
.row.target-row {
flex: 1;
display: flex;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet" />
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
<h1>
HEADER
</h1>
</div>
<div class="large-6 columns">
<h1>
menu
</h1>
</div>
</div>
<div class="row target-row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
flex-grow only applies to flex children.
.image-container isn't a direct child of a display: flex element, so that property has no effect.
Plus, it affects the flex axis, which is not what you want.
Instead, you need to put those two elements in their own flex row, and use align-items (on the parent) and align-self (on either child) so that the first one aligns (on the cross axis) to flex-start (stick to top) and the second one to stretch.
You'll also want that flex row (parent) to have flex-grow: 1 so that it stretches along the vertical flex axis of its parent (.wrapper) to fill the rest of the page (otherwise, the grandchild will have nothing to stretch to).
For more information, read a good flex tutorial.
div.wrapper > div:not(.header).row {
flex: 1; /* 1 */
display: flex; /* 1 */
}
div.large-7.columns {
display: flex; /* 2 */
}
div.image-container { /* 3 */
flex: 1;
}
div.large-5.show-for-medium { /* 4 */
align-self: flex-start;
}
jsFiddle
Notes:
flex container and items consume all remaining height of respective parents
give children full height (via align-items: stretch initial setting)
flex item consumes all available width
yellow box does not need to expand to full height; now set to content height