Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Absolutely positioned flex item is not removed from the normal flow in IE11

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.

enter image description here

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>
like image 422
Nikolay Aleshkovskiy Avatar asked Oct 07 '15 11:10

Nikolay Aleshkovskiy


3 Answers

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>
like image 103
Michael Benjamin Avatar answered Nov 17 '22 22:11

Michael Benjamin


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

like image 16
Guru Avatar answered Nov 18 '22 00:11

Guru


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.

like image 6
Emanuele Feliziani Avatar answered Nov 17 '22 22:11

Emanuele Feliziani