I'm trying out CSS3's flexbox for the first time and it seems promising, but I'm having some trouble getting it to behave.
Basically, I want flexbox to behave like a table but with an order property so I can tell it in the CSS what order to display the elements in the grid. Is this even possible with flexbox?
Here's the test page I created: https://jsfiddle.net/Lb838dwf/
HTML:
<div id="main">
<div id="div1">div 1</div>
<div id="div2">div 2</div>
<div>generic</div>
<div id="div3">div 3</div>
<div>generic</div>
<div id="div4">div 4</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
</div>
CSS:
#main {
width: 100%;
border: 1px solid #c3c3c3;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
#main div {
min-width: 25%;
height: 100px;
-webkit-flex: 1;
flex: 1;
-webkit-order: 2;
order: 1;
background-color: #ccc;
}
#main #div1 {
-webkit-order: 3;
order: 3;
background-color: coral;
}
#main #div2 {
-webkit-order: 5;
order: 5;
background-color: lightblue;
}
#main #div3 {
-webkit-flex: 2;
flex: 2;
-webkit-order: 2;
order: 2;
background-color: lightgreen;
}
#main #div4 {
-webkit-order: 4;
order: 4;
background-color: pink;
}
You can see that #div3 has flex: 2, which means it should span two columns, but it's only taking up 1 column. Also, the 3 divs at the bottom (div1, div4, div2) aren't aligning to the same grid as the items above it. If I add max-width: 25% to the #main div style, it keeps everything to the same grid, but spanning columns doesn't work. I tried setting max-width: 25% for all divs and max-width: none for just #div3 (the one with flex:2) but that doesn't have an affect.
Let's dissect a few of the things you wrote so we could clarify flexbox behavior.
You can see that #div3 has flex: 2, which means it should span two
columns, but it's only taking up 1 column.
flex: 2 does not mean it should span two columns. This isn't like an HTML colspan attribute. (And even if it was, you have three other flex items ["table cells"] already occupying the three remaining columns, so how would #div3 expand two columns? It would have to break out of the grid.)
The flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.
The flex-grow property controls how flex items will expand by distributing remaining space in the flex container. So by applying flex: 2 you're saying you want the flex item to take twice as much remaining space than its siblings, not necessarily be double their size.
From the spec, the flex-grow property:
...determines how much the flex item will grow relative to the rest of
the flex items in the flex container when positive free space is
distributed.
However, since you've given the container (#main) a width: 100%, and each flex item min-width: 25%, there is no remaining space to distribute. So nothing happens.
To illustrate this behavior, change the width of each flex item to 50px. This leaves extra space to distribute and #div3 takes 2x as much. See demo: https://jsfiddle.net/Lb838dwf/6/
Also, the 3 divs at the bottom (div1,div4,div2) aren't aligning to
the same grid as the items above it.
Correct. They're not aligning because you applied flex: 1 to them in #main div. This tells them to evenly distribute all remaining space among themselves.
If I add max-width: 25% to the #main div style, it keeps
everything to the same grid, but spanning columns doesn't work. I
tried setting max-width: 25% for all divs and max-width: none for
just #div3 (the one with flex:2) but that doesn't have an affect.
What? You lost me.
I want flexbox to behave like a table but with an order property so I
can tell it in the CSS what order to display the elements in the grid.
Is this even possible with flexbox?
Yes, it's possible.
HTML
<div id="main">
<div>div 1</div>
<div>div 2</div>
<div>div 3</div>
<div>div 4</div>
<div>div 5</div>
<div>div 6</div>
<div>div 7</div>
<div>div 8</div>
<div>div 9</div>
<div>div 10</div>
<div>div 11</div>
</div>
CSS
#main {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
width: 100%;
}
#main div {
flex: 0 0 150px; /* don't grow, don't shrink, stay at 150px */
height: 100px;
margin: 5px;
background-color: #ccc;
border: 1px dashed black;
}
#main div:nth-child(2) { order: -4; background-color: lightblue; }
#main div:nth-child(5) { order: -3; background-color: lightgreen; }
#main div:nth-child(8) { order: -2; background-color: lightyellow; }
#main div:nth-child(11) { order: -1; background-color: lightpink; }
DEMO: http://jsfiddle.net/Lb838dwf/4/
Spanning multiple columns
As mentioned above, the flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.
flex-grow tells flex items how to distribute available space, hence it is not a reliable tool for emulating the HTML colspan attribute. (The space available in the container and double the size of a flex item are unrelated lengths and not necessarily equal.)
However, the flex-basis property, which sets the initial size of a flex item, can be used to make flex items twice as wide, three times as wide, whatever.
In my code above, flex items are set to: flex: 0 0 150px; The third value represents flex-basis. So each box is set to 150px wide.
For a flex item to occupy two columns simply double that value.
Since flex cascades to all divs, we only need to adjust the flex-basis for targeted items.
#main div:nth-child(11) { flex-basis: calc(300px + 10px); } /* factoring in margin space */
#main div:nth-child(7) { flex-basis: calc(450px + 20px); } /* factoring in margin space */
DEMO: http://jsfiddle.net/Lb838dwf/5/ (expand window for effect)
Related
I want to use Flexbox that has some number of items that are all the same width. I've noticed that Flexbox distributes the space around evenly, rather than the space itself.
For example:
.header {
display: flex;
}
.item {
flex-grow: 1;
text-align: center;
border: 1px solid black;
}
<div class="header">
<div class="item">asdfasdfasdfasdfasdfasdf</div>
<div class="item">z</div>
</div>
The first item is a lot bigger than the second. If I have three items, four items, or n items, I want them all to appear on the same line with an equal amount of space per item.
Any ideas?
http://codepen.io/anon/pen/gbJBqM
Set them so that their flex-basis is 0 (so all elements have the same starting point), and allow them to grow:
flex: 1 1 0px;
Your IDE or linter might mention that the unit of measure 'px' is redundant. If you leave it out (like: flex: 1 1 0), IE will not render this correctly. So the px is required to support Internet Explorer, as mentioned in the comments by #fabb;
You need to add width: 0 to make columns equal if contents of the items make it grow bigger.
.item {
flex: 1 1 0;
width: 0;
}
Detail:
flex: 1 1 0 is the same as
flex-grow: 1; flex-shrink: 1; flex-basis: 0;
and if the parent container can not provide enough space for the native-size added together of every item (no space to grow), we need to make the width: 0 to give every item the same start point to grow.
You could add flex-basis: 100% to achieve this.
Updated Example
.header {
display: flex;
}
.item {
flex-basis: 100%;
text-align: center;
border: 1px solid black;
}
For what it's worth, you could also use flex: 1 for the same results as well.
The shorthand of flex: 1 is the same as flex: 1 1 0, which is equivalent to:
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
text-align: center;
border: 1px solid black;
}
The accepted answer by Adam (flex: 1 1 0) works perfectly for flexbox containers whose width is either fixed, or determined by an ancestor. Situations where you want the children to fit the container.
However, you may have a situation where you want the container to fit the children, with the children equally sized based on the largest child. You can make a flexbox container fit its children by either:
setting position: absolute and not setting width or right, or
place it inside a wrapper with display: inline-block
For such flexbox containers, the accepted answer does NOT work, the children are not sized equally. I presume that this is a limitation of flexbox, since it behaves the same in Chrome, Firefox and Safari.
The solution is to use a grid instead of a flexbox.
When you run this snippet, make sure to click on full page to see the effect properly.
body {
margin: 1em;
}
.wrap-inline-block {
display: inline-block;
}
#div0, #div1, #div2, #div3, #div4 {
border: 1px solid #888;
padding: 0.5em;
text-align: center;
white-space: nowrap;
}
#div2, #div4 {
position: absolute;
left: 1em;
}
#div0>*, #div1>*, #div2>*, #div3>*, #div4>* {
margin: 0.5em;
color: white;
background-color: navy;
padding: 0.5em;
}
#div0, #div1, #div2 {
display: flex;
}
#div0>*, #div1>*, #div2>* {
flex: 1 1 0;
}
#div0 {
margin-bottom: 1em;
}
#div2 {
top: 15.5em;
}
#div3, #div4 {
display: grid;
grid-template-columns: repeat(3,1fr);
}
#div4 {
top: 28.5em;
}
<p>Normal scenario — flexbox where the children adjust to fit the container — and the children are made equal size by setting {flex: 1 1 0}</p>
<div id="div0">
<div>
Flexbox
</div>
<div>
Width determined by viewport
</div>
<div>
All child elements are equal size with {flex: 1 1 0}
</div>
</div>
<p>Now we want to have the container fit the children, but still have the children all equally sized, based on the largest child. We can see that {flex: 1 1 0} has no effect.</p>
<div class="wrap-inline-block">
<div id="div1">
<div>
Flexbox
</div>
<div>
Inside inline-block
</div>
<div>
We want all children to be the size of this text
</div>
</div>
</div>
<div id="div2">
<div>
Flexbox
</div>
<div>
Absolutely positioned
</div>
<div>
We want all children to be the size of this text
</div>
</div>
<br><br><br><br><br><br>
<p>So let's try a grid instead. Aha! That's what we want!</p>
<div class="wrap-inline-block">
<div id="div3">
<div>
Grid
</div>
<div>
Inside inline-block
</div>
<div>
We want all children to be the size of this text
</div>
</div>
</div>
<div id="div4">
<div>
Grid
</div>
<div>
Absolutely positioned
</div>
<div>
We want all children to be the size of this text
</div>
</div>
I’m not an expert with Flexbox, but I got there by setting the basis to 50% for the two items I was dealing with. Grow to 1 and shrink to 0.
Inline styling: flex: '1 0 50%',
None of these solutions worked for me, but this did:
.header {
display: flex;
}
.item {
width: 100%;
}
/* Demo styles, for aesthetics. */
.demo {
margin: 3rem;
}
.demo .item {
text-align: center;
padding: 3rem;
background-color: #eee;
margin: 0 1.5rem;
}
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
<div class="item">
3
</div>
</div>
</div>
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
</div>
</div>
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
<div class="item">
3
</div>
<div class="item">
4
</div>
<div class="item">
5
</div>
</div>
</div>
None of these answers solved my problem, which was that the items weren't the same width in my makeshift flexbox table when it was shrunk to a width too small.
The solution for me was simply to put overflow: hidden; on the flex-grow: 1; cells.
On the child element of flex,
flex: 1 1 25%
this will allow to have four items.
If you want to add more items then you can decrease the %.
This will work even if you wrapping items, like a Grid, but not so simple you should show where it will wrap in media queries)).
Example:
.flex-item {
flex: 0 0 calc(25% - (45px / 4))
}
It works like this:
$n: 4; // Number of columns
$gap: 15px; // Margin pixels
.flex-parent {
display: flex;
gap: $gap;
}
.flex-item {
flex: 0 0 calc(100% / $n - (($n - 1) * $gap / $n));
}
I was having a similar issue and found a way to cheat.
As others have said, flex-basis and flex-grow: 1 are the way to keep items the same size, but the last row is an exception when there are too few items (they fill all available space, making them larger than the other items). To stop this from happening, I added an empty spacer item with visibility: hidden and set the flex-grow value inline based on a quick calculation of how many items there were.
This is easier if you know the width of the parent container but even if you don't, you can set the flex-grow value on the spacer using media queries and breakpoints.
I want to use Flexbox that has some number of items that are all the same width. I've noticed that Flexbox distributes the space around evenly, rather than the space itself.
For example:
.header {
display: flex;
}
.item {
flex-grow: 1;
text-align: center;
border: 1px solid black;
}
<div class="header">
<div class="item">asdfasdfasdfasdfasdfasdf</div>
<div class="item">z</div>
</div>
The first item is a lot bigger than the second. If I have three items, four items, or n items, I want them all to appear on the same line with an equal amount of space per item.
Any ideas?
http://codepen.io/anon/pen/gbJBqM
Set them so that their flex-basis is 0 (so all elements have the same starting point), and allow them to grow:
flex: 1 1 0px;
Your IDE or linter might mention that the unit of measure 'px' is redundant. If you leave it out (like: flex: 1 1 0), IE will not render this correctly. So the px is required to support Internet Explorer, as mentioned in the comments by #fabb;
You need to add width: 0 to make columns equal if contents of the items make it grow bigger.
.item {
flex: 1 1 0;
width: 0;
}
Detail:
flex: 1 1 0 is the same as
flex-grow: 1; flex-shrink: 1; flex-basis: 0;
and if the parent container can not provide enough space for the native-size added together of every item (no space to grow), we need to make the width: 0 to give every item the same start point to grow.
You could add flex-basis: 100% to achieve this.
Updated Example
.header {
display: flex;
}
.item {
flex-basis: 100%;
text-align: center;
border: 1px solid black;
}
For what it's worth, you could also use flex: 1 for the same results as well.
The shorthand of flex: 1 is the same as flex: 1 1 0, which is equivalent to:
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
text-align: center;
border: 1px solid black;
}
The accepted answer by Adam (flex: 1 1 0) works perfectly for flexbox containers whose width is either fixed, or determined by an ancestor. Situations where you want the children to fit the container.
However, you may have a situation where you want the container to fit the children, with the children equally sized based on the largest child. You can make a flexbox container fit its children by either:
setting position: absolute and not setting width or right, or
place it inside a wrapper with display: inline-block
For such flexbox containers, the accepted answer does NOT work, the children are not sized equally. I presume that this is a limitation of flexbox, since it behaves the same in Chrome, Firefox and Safari.
The solution is to use a grid instead of a flexbox.
When you run this snippet, make sure to click on full page to see the effect properly.
body {
margin: 1em;
}
.wrap-inline-block {
display: inline-block;
}
#div0, #div1, #div2, #div3, #div4 {
border: 1px solid #888;
padding: 0.5em;
text-align: center;
white-space: nowrap;
}
#div2, #div4 {
position: absolute;
left: 1em;
}
#div0>*, #div1>*, #div2>*, #div3>*, #div4>* {
margin: 0.5em;
color: white;
background-color: navy;
padding: 0.5em;
}
#div0, #div1, #div2 {
display: flex;
}
#div0>*, #div1>*, #div2>* {
flex: 1 1 0;
}
#div0 {
margin-bottom: 1em;
}
#div2 {
top: 15.5em;
}
#div3, #div4 {
display: grid;
grid-template-columns: repeat(3,1fr);
}
#div4 {
top: 28.5em;
}
<p>Normal scenario — flexbox where the children adjust to fit the container — and the children are made equal size by setting {flex: 1 1 0}</p>
<div id="div0">
<div>
Flexbox
</div>
<div>
Width determined by viewport
</div>
<div>
All child elements are equal size with {flex: 1 1 0}
</div>
</div>
<p>Now we want to have the container fit the children, but still have the children all equally sized, based on the largest child. We can see that {flex: 1 1 0} has no effect.</p>
<div class="wrap-inline-block">
<div id="div1">
<div>
Flexbox
</div>
<div>
Inside inline-block
</div>
<div>
We want all children to be the size of this text
</div>
</div>
</div>
<div id="div2">
<div>
Flexbox
</div>
<div>
Absolutely positioned
</div>
<div>
We want all children to be the size of this text
</div>
</div>
<br><br><br><br><br><br>
<p>So let's try a grid instead. Aha! That's what we want!</p>
<div class="wrap-inline-block">
<div id="div3">
<div>
Grid
</div>
<div>
Inside inline-block
</div>
<div>
We want all children to be the size of this text
</div>
</div>
</div>
<div id="div4">
<div>
Grid
</div>
<div>
Absolutely positioned
</div>
<div>
We want all children to be the size of this text
</div>
</div>
I’m not an expert with Flexbox, but I got there by setting the basis to 50% for the two items I was dealing with. Grow to 1 and shrink to 0.
Inline styling: flex: '1 0 50%',
None of these solutions worked for me, but this did:
.header {
display: flex;
}
.item {
width: 100%;
}
/* Demo styles, for aesthetics. */
.demo {
margin: 3rem;
}
.demo .item {
text-align: center;
padding: 3rem;
background-color: #eee;
margin: 0 1.5rem;
}
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
<div class="item">
3
</div>
</div>
</div>
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
</div>
</div>
<div class="demo">
<div class="header">
<div class="item">
1
</div>
<div class="item">
2
</div>
<div class="item">
3
</div>
<div class="item">
4
</div>
<div class="item">
5
</div>
</div>
</div>
None of these answers solved my problem, which was that the items weren't the same width in my makeshift flexbox table when it was shrunk to a width too small.
The solution for me was simply to put overflow: hidden; on the flex-grow: 1; cells.
On the child element of flex,
flex: 1 1 25%
this will allow to have four items.
If you want to add more items then you can decrease the %.
This will work even if you wrapping items, like a Grid, but not so simple you should show where it will wrap in media queries)).
Example:
.flex-item {
flex: 0 0 calc(25% - (45px / 4))
}
It works like this:
$n: 4; // Number of columns
$gap: 15px; // Margin pixels
.flex-parent {
display: flex;
gap: $gap;
}
.flex-item {
flex: 0 0 calc(100% / $n - (($n - 1) * $gap / $n));
}
I was having a similar issue and found a way to cheat.
As others have said, flex-basis and flex-grow: 1 are the way to keep items the same size, but the last row is an exception when there are too few items (they fill all available space, making them larger than the other items). To stop this from happening, I added an empty spacer item with visibility: hidden and set the flex-grow value inline based on a quick calculation of how many items there were.
This is easier if you know the width of the parent container but even if you don't, you can set the flex-grow value on the spacer using media queries and breakpoints.
I am trying to place child elements in parent element using flexbox like in this image -- >
https://ibb.co/Wpph8fP.
I know that this is possible by doing three columns then use flex, then flex-column in the first column and center other two vertically.
The thing is, that I need do this in one parent div. 4th element is wrapped and it creates column with 1st element, then I need to center vertically two other elements.
This is what I have already done:
the code is here https://codepen.io/MrEyelet/pen/wLxygN?editors=1100
To use one flex, first set flex-direction to column and set wrap toflex-wrap.
And, the part that needs to be on two lines has a variable height to avoid line breaks.
Next, set the flex property to none, setting margin enough to make a line break in the part that needs to be on one line. This will force flex items to break and keep the size of the item.
flex - Values[mdn]
none
The item is sized according to its width and height properties. It is fully inflexible: it neither shrinks nor grows in relation to the flex container. This is equivalent to setting "flex: 0 0 auto".
body {
background: tomato;
margin: 0;
}
.parent {
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
height: 100vh;
}
.child {
width: 30%;
height: 200px;
margin: 20px 0;
background: indigo;
}
/* add below */
.child:nth-of-type(-n+2) {
flex: 1 0 calc(50% - 40px);
}
/* add below */
.child:nth-of-type(n+3) {
flex: none;
margin: 50% 0;
}
<div class="parent">
<div class="child child-1">
</div>
<div class="child child-2">
</div>
<div class="child child-3">
</div>
<div class="child child-4">
</div>
</div>
Currently, I'm trying to make a list. On the left side images which fit into my "display flex layout" and resize on all browsers. My "flex-grow layout" list 1 for the image and 3 for the description. Without images, everything is fine, but with my image (100% width) it does not fit into the 1/3 of the row ...
Does anybody know a solution?
#main {
height: 100px;
border: 1px solid #c3c3c3;
display: -webkit-flex; /* Safari */
display: flex;
}
#main div:nth-of-type(1) {-webkit-flex-grow: 1;}
#main div:nth-of-type(2) {-webkit-flex-grow: 3;}
#main div:nth-of-type(1) {flex-grow: 1;}
#main div:nth-of-type(2) {flex-grow: 3;}
#main img { width: 100% }
<div id="main">
<div style="background-color:coral;"><img src="http://www.der-eyecatcher.de/wp-content/uploads/2016/02/Lampe-silber-Schirm_1.jpg"></div>
<div style="background-color:lightblue;">Description</div>
</div>
<hr>flex-grow without Image<hr>
<div id="main">
<div style="background-color:coral;"></div>
<div style="background-color:lightblue;">Description</div>
</div>
Thank you
Simon
You have the image container – the flex item div – set to flex-grow: 1. That's fine.
Except the default value of flex-basis is auto (spec).
So, you're telling the item that it's initial main size (i.e., flex-basis) should be the size of its content (the image). That's exactly what's happening. The item takes the natural size of the image.
Add this to your code:
flex-basis: 0
OR, better yet, this:
flex: 1
which is short for:
flex: 1 1 0
which is short for:
flex-grow: 1
flex-shrink: 1
flex-basis: 0
From the spec:
7.2. Components of
Flexibility
Authors are encouraged to control flexibility using the flex shorthand
rather than with its longhand properties directly, as the shorthand
correctly resets any unspecified components to accommodate common
uses.
For an explanation of the difference between flex-basis: auto and flex-basis: 0, see this post:
Make flex-grow expand items based on their original size
#main {
height: 100px;
border: 1px solid #c3c3c3;
display: flex;
}
/* adjustment here */
#main div:nth-of-type(1) {
flex: 1;
}
#main div:nth-of-type(2) {
flex-grow: 3;
}
#main img {
width: 100%;
height: auto;
}
<div id="main">
<div style="background-color:coral;"><img src="http://www.der-eyecatcher.de/wp-content/uploads/2016/02/Lampe-silber-Schirm_1.jpg"></div>
<div style="background-color:lightblue;">Description</div>
</div>
<hr>flex-grow without Image
<hr>
<div id="main">
<div style="background-color:coral;"></div>
<div style="background-color:lightblue;">Description</div>
</div>
I have a simple flex-box layout and I would like the middle item to shrink right down, but I cannot get any situation where the flex-shrink property works.
I've tried removing the flex-wrap property on the flex-container and changed the values of the flex items, but it still doesn't work?
In the code below, how do I get the middle div to shrink to a flex-shrink value of 10 like the code would suggest it should do?
Many thanks in advance for any help.
body {
padding: 20px;
font-family: arial;
}
p {
color: white;
}
.container {
background-color: #666;
max-width: 800px;
height: 800px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
padding: 10px;
box-sizing: border-box;
}
.item1 {
flex: 1 1 0;
background-color: red;
min-width: 200px;
}
.item2 {
flex: 2 10 0;
background-color: blue;
}
.item3 {
flex: 1 1 0;
background-color: green;
}
<section class="container">
<div class="item item1">
<h1>ITEM1</h1>
</div>
<div class="item item2">
<h1>ITEM2</h1>
</div>
<div class="item item3">
<h1>ITEM3</h1>
<p> - this is the third item with more content</p>
</div>
</section>
https://codepen.io/emilychews/pen/rYeoza
That's not how flex-shrink works.
The job of flex-shrink is to distribute negative free space in the container among items. This means it will shrink items proportionally when the items would otherwise overflow the container.
But there is no overflow condition in your container.
The first column (.item1) is 200px in width.
Your second column (.item2) has no fixed width. It is set to consume free space.
Your third column (.item3) has no fixed width. It is also set to consume free space.
Therefore, flex-shrink has nothing to do. In a container with width: 800px, where only 200px is used, flex-shrink is never called into action.
You have flex-shrink enabled on your three columns (with values 1, 10 and 1, respectively). If you disable flex-shrink on all of them you'll notice that it makes no difference to the layout.
Here's your demo with flex-shrink: 0 applied to all three times: revised codepen
For a detailed explanation of how flex-shrink works, see this post:
How does flex-shrink factor in padding and border-box?