We have two divs with content and a third div that is a background with absolute position.
Container is a flexbox.
All works fine in Chrome and Safari, but <
As an alternative you can use the flex property inside the content selector(s):
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50; top: 20px;
display: flex;
flex: 1; /* add this */
}
This will set the flex-grow. It might not be exactly what you need, but maybe it helps somebody else that can not reorder the content divs or take them out from the flex wrapper.
Here is the demo: https://jsfiddle.net/s18do03e/14/
UPDATE: This issue has been resolved in Firefox (as of v52, released March 2017). The problem still exists in IE11.
Like you wrote in the question:
Firefox calculates absolute positioned div, and distributes space between divs like there are 3 divs in a row.
Firefox is considering the third div (.bg
), which is absolutely positioned, an in-flow flex item and factoring it into its space-between
calculation. (IE11 does this, too; Chrome and Edge ignore it.)
Clearly, this is not in compliance with the current flexbox spec:
4.1. Absolutely-Positioned Flex Children
As it is out-of-flow, an absolutely-positioned child of a flex container does not participate in flex layout.
Here are some workarounds:
Why not move the absolutely positioned div between the other two?
Instead of this:
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
<div class="bg">Background</div>
</div>
Try this:
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
OR... remove .bg
from the flex container:
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
</div>
<div class="bg">Background</div>
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
</div>
<div class="bg">Background</div>
OR... use the flex order
property to re-arrange the flex items.
Add this to your code:
.c2 { order: 1; }
div.container {
display: flex;
flex-direction: row;
width: 100%;
height: 300px;
justify-content: space-between;
width: 100%;
outline: 1px solid;
}
div.c1 {
background: #aaeecc;
width: 100px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
}
div.c2 {
background: #cceeaa;
width: 200px;
position: relative;
z-index: 50;
top: 20px;
display: flex;
order: 1;
}
div.bg {
background: #ccc;
width: 100%;
height: 100%;
z-index: 0;
left: 0px;
top: 0px;
position: absolute;
display: flex;
}
<div class="container">
<div class="c1">Content 1</div>
<div class="c2">Content 2</div>
<div class="bg">Background</div>
</div>
It is happening because justify-content: space-between;
Distribute items evenly The first item at the start, the last at the end. So just putt <div class="bg">Background</div>
between <div class="c1">Content 1</div>
and <div class="c2">Content 2</div>
like this
<div class="container">
<div class="c1">Content 1</div>
<div class="bg">Background</div>
<div class="c2">Content 2</div>
</div>
You can see the reason on https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content
Sometimes it is not possible to re-order stuff, for example when using ::before
and ::after
. In those cases, you can manually order
the elements.
In your case, you would need to do:
.c1 {
order: -1;
}
.c2 {
order: 10;
}
The order
property is part of the flex
spec and lets you re-order flex items (reference on MDN). It's very handy for multiple purposes, this included.
I used -1
because the value is ordinal, so setting it to a negative number ensures it precedes all other defaults and you don't need to specify the value for ::before
. For the same reason, using 10
ensures that the second div comes last even if you add a bunch of elements to the container. You can increase that to 100
or whatever.
Still, Firefox's behaviour seems counterintuitive. position: absolute
normally removes the element for the usual dom flow and I would expect that element to be removed from the flex
flow as well, just like in Safari and Chrome. I am not sure whether the spec clarify this.