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 Firefox and IE11 factors in the absolute positioned div, and distributes space between divs like there are 3 divs in a row.
I've made jsfiddle example. Is there any way to fix this bug? https://jsfiddle.net/s18do03e/2/
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 class="bg">Background</div>
</div>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With