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

4

29

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>
Fayalite answered 7/10, 2015 at 11:30 Comment(1)
Absolute positioning inside flex-container as of dec. 2019: codepen.io/ekadagami/pen/mdyPybqApostles
R
16

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

Ranique answered 7/10, 2015 at 11:59 Comment(0)
B
37

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>
Bronchiole answered 7/10, 2015 at 11:54 Comment(9)
this is an incredibly thorough answer. Spot on mate.Preshrunk
Note that Firefox (and IE) have this behavior because it was required by an older revision of the flexbox spec -- the spec text on abspos flex children has changed quite a bit over time. Good news, though -- the current spec behavior will ship in Firefox 52! (which is slated for release on March 7th 2017)Dyslalia
Well done and best answer here. I'd suggest using the "order" property before the other suggestions as this is really a CSS question, and source ordering a background div last is better and removing it from the container might not be possible.Ted
What is order doing here exactly?Bakken
It's moving the .c2 element, which is second in the source, to the third position (more info). @DrazenBjelovukBronchiole
Hmm, does this only work with 3 elements? I'm trying to replicate the fix in a similar scenario with 5, but it still seems to take up space in IE (even when all together moving it into the center).Bakken
In any case, moving it out seems the way to go for me anyway as my component houses a variable number of flex-items wherein ensuring the order in all cases appears beyond the limits of css.Bakken
The default order value is 0 for all flex items. If you change the value of one item to 1, it puts it in last position (-1 would put it in first position). If you want to move an item between other items, you would need to adjust the order value for all items (1, 2, 3, etc.), in order to shift them around. @DrazenBjelovukBronchiole
Yep I got that, the ordering was fine (like I said, still didn't seem to work after moving the element altogether, the extra space just shifted to the middle). But anyway, I've gone with the moving it out approach and that fits my needs fine. Great answer either way.Bakken
R
16

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

Ranique answered 7/10, 2015 at 11:59 Comment(0)
D
6

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.

Duplicity answered 1/3, 2016 at 20:24 Comment(0)
O
1

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/

Opalescent answered 29/2, 2016 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.