Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude element with fixed positioning from justify-content in flex layout [duplicate]

Tags:

html

css

flexbox

I'm currently trying to build a responsive website layout with flexbox.

Depending on the screen size I want an element to have position: fixed; This itself is working. But when I use justify-content: space-between; on a column that contains an element that gets moved out of the of column itself with position: fixed; the space distribution uses this element as an 0 height element.

For the sake of demonstration I set up two examples. Both do not use media queries because they are not the problem.

In the first example I created what I want the final result to look like. I had to move #fixed outside of .column in order for the space distribution to work properly.

In the second example I created the desired HTML markup. When you compare both example side by side you will notice that the spacing looks odd in the second. The browser is not making an error here because there is an element it has to respect when distributing the space. I (in a nutshell) would like the browser to pretend #fixed is not inside the container and therefore not consider it when distributing space.

This is how the result should look: https://jsfiddle.net/5nu9nsyL/3/

And this is how the html should look: https://jsfiddle.net/5nu9nsyL/4/
(Only Chrome and Safari renders it correctly. So if both look the same to you have a look at it with a different browser like Firefox or IE)

I hope I made my it clear what I want.

(Note I must use display: flex on the container .column)

(I also need a JavaScript free, CSS only solution)

like image 370
BrainStone Avatar asked Feb 08 '23 12:02

BrainStone


2 Answers

This is Firefox bug 874718. The spec says

The justify-content property aligns flex items along the main axis of the current line of the flex container.

Since an absolutely (including fixedly) positioned element is out-of-flow, is not a flex item (this is fully defined in Absolutely-Positioned Flex Children). So justify-content should ignore it.

But Firefox wraps it inside an anonymous flex item, according to an old spec:

Absolutely positioned children of a flex container are not themselves flex items, but they leave behind "placeholders" in their normal position in the box tree. These placeholders are anonymous inline boxes with a width, height, and line-height of ‘0’, and they interact normally with the flexbox layout algorithm. In particular, they'll trigger the creation of anonymous flex items, or join neighboring inline elements in their anonymous flex items.

To fix that, instead of using justify-content, I recommend aligning with auto margins:

.column > div:not(:first-child) {
  margin-top: auto;
}

That will distribute free space equally before the children of the flex container, except the first one. The effect should be like justify-content: space-between.

In the case of the fixed element, that auto margin will just compute to 0.

.column {
  display: flex;
  flex-flow: column nowrap;
  height: 300px;
  float: left;
  margin: 10px;
  border: 3px solid black;
}
.column > div:not(:first-child) {
  margin-top: auto;
}
.column > div,
#fixed {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 200px;
  height: 50px;
  background-color: red;
}
#fixed {
  position: fixed;
  top: 0;
  right: 0;
}
<div class="column">
  <div>Element 1</div>
  <div id="fixed">Element 2</div>
  <div>Element 3</div>
  <div>Element 4</div>
  <div>Element 5</div>
</div>
like image 144
Oriol Avatar answered Feb 13 '23 06:02

Oriol


Update

Upon reviewing the OP once more, this is the objective:

When the screen width is at a certain size (I determined 720px+), #fixed(col2 ele2) is position: fixed, if it's at a smaller width, it will be position: static. When #fixed is "fixed" it is no longer in col2, therefore the spacing between col2's children increase. What is desired is a consistent spacing between all divs (ie col2 spacing must coincide with col1 spacing).

The OP did not provide a means to show both states: fixed and static; of which static needed to be established. I have added two media queries that will insure both static and fixed states of #fixed will be triggered at 720px.

@media screen and (min-width: 721px) {
  .spacer {
    display: none;
  }
  #fixed {
    position: fixed;
  }
  .column.column {
    justify-content: space-between !important;
  }
}

@media screen and (max-width: 720px) {
  .spacer.spacer {
    display: none !important;
  }
  #fixed {
    position: static;
  }
  .column.column {
    justify-content: space-between !important;
  }
}

For some reason, when applying just the properties display and position, the justify-content: space-between would break. So I included justify-content: space-between to both media queries, unfortunately that didn't work either. So then I added !important, and it still failed. So then I used my secret ninja technique and doubled the class selector .column.column and I was victorious! Doubling up on a selector will increase it's specitivity to trump anything.

Plunker in Full Screen Mode Resize the browser to see the magic.

Plunker in Preview Mode


I don't understand what you want to with #fixed. But I do understand the problem described in the demo:

The space between Element 1 and Element 3 in the second column should be the same height as the other spaces.

Since the second column cannot arbitrarily space it's content by a predetermined length due to the nature of justify-content: space-between, you'll need just as many divs as the first column in order to obtain the same spacing as the first column. As I understand it, you cannot hard code layout (HTML), so I wrote a little JS to create an invisible div and append it to the second column thereby making the space between element 1 and 3 the same as the spaces between the children of the first column.

Fiddle

like image 23
zer00ne Avatar answered Feb 13 '23 05:02

zer00ne