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

This question already has answers here:
Absolutely positioned flex item is not removed from the normal flow in IE11
(4 answers)
Closed 6 years ago.
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)

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>

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

Related

Pure CSS: centering vertically and horizontally an absolute positioned element with width depending on children

So... I got this code: https://jsfiddle.net/jmg63s3e/1/
The code actually works fine if you resize the browser window until you have the text inline with the image and that's what I'm trying to achieve, but if you resize it down eventually the text drops below the image even if the wrapper width is a lot smaller than the window width.
My only purpose is to have:
the whole wrapper centered both vertically and horizontally in the browser window. Its total width and height unknown, depending on its children
row1 and row2 must not be inline: row2 must be below row1
All the elements inside row1 (the image and the text containing 2 spans) must be inline with each other
And well, the spinner inside row2 must also be centered inside the row but that was never a problem whatever solution I tried
As a matter of fact the only dynamic element in the whole code is the first span which in the example contains Player #1, since it should be the name of the player and it can be anything, any length.
Of course if I wanna make it responsive I will have to use media queries or dynamically change widths and heights and font-sizes with JS, and I'm willing to do so. My problem here is only the wrapper itself and the text that drops below the image even if the wrapper width is a lot smaller than the window width, so I'm asking for a solution that works as long as the wrapper width is smaller than the window width. When the wrapper width drops below the window width, I will handle the style with responsive media queries or JS. I would just like to have the wrapper to be centered both vertically and horizontally in the window, and its size to be dynamic and depending on children.
I've already tried any solution I could think of, but with an unknown wrapper width I just can't figure it out. Can someone help me please? I'm open to any suggestion and any solution, as long as it's pure CSS and it doesn't involve JS. Thanks everyone in advance
You can use flexbox to fix these problems.
Here's an updated fiddle with old CSS commented out: https://jsfiddle.net/jmg63s3e/3/
First, to align the wrapper both horizontally and vertically you need to make the parent container a flex container with display: flex and use justify-content: center and align-items: center. You also need to set a height or else it will wrap to the height of the child and not give you the centering effect. I used the following. The height can be whatever you need it to be.
.trump-waiting {
display: flex;
justify-content: center;
align-items: center;
font-size: 0;
overflow: hidden;
height: 100vh;
}
Next, I used display: flex on the wrapper and flex-direction: column to make sure they are all lined up like we want them to be.
.trump-waiting .wrapper {
display:flex;
flex-direction:column;
To fix row1, again I used flexbox and removed the inline-block and the set height. You could set the height as long as you take care of resizing the font in the text divs, with media queries for instance. Otherwise, with an explicit height, the font at the size it's at now will break out of their containers. Without explicitly setting the height, the containers will adjust in size.
.trump-waiting .row1 {
display: flex;
flex-direction: row;
/* display: inline-block; */
/* height: 60px; */
background-color: yellow;
}
I also added flex-shrink:0 to .image to keep it from shrinking on resize.
To keep Player #1 and 'is choosing the trump suit' inline, I also added display: flex and flex-direction: row to .row keep them on the same line.
Finally, to align the loader, I did the vertical/horizontal alignment trick used above, plus added some padding to the div to give it some space and removed the old css.
.trump-waiting .row2 {
display: flex;
justify-content: center;
align-items: center;
padding: 16px 0 16px 0;
/* display: block; */
/* margin-top: 50px; */
The last step would be to use media queries to adjust the font-sizes on .text spans so the font doesn't expand their container on resize.
Many ways to skin a cat and I'm sure others will have different perhaps better solutions, but hope this helps. There's a great summary of flexbox here if you need it. I may have left out a change in this summary, but it should all be in the fiddle.
EDIT: Realized I made a mistake summarizing the css in the jsfiddle and also removed a redundant css property. Now updated.

CSS extend one element of a group of DIV

I'm currently trying to get an element (div) stretching itself over the free space of a parent element while respecting the size of other elements on its level. I found some solutions and tried most of them but I couldn't get it to work. I suspect this is because of the cms I'm working with which - when telling it to make a set of columns the same height - changes the parent display-style to table-cell. So... here is an image of what I'm trying to archive.
As said, the CMS changes the blue container to display: table-cell to stretch it over the whole area and make all columns in a row the same height. Inside of this blue container are the elements I can control. These are up to four div (white/green) inside of a parent div (yellow). The white div are dynamic and not always present and the green one needs to stretch over the whole vertical space no matter which of the white elements are present.
And idea how to accomplish that? I tried a lot of answers about this topic but they didnt work.. I think that's maybe due to the fact that the blue container is a table-cell?
edit: Here is what I got so far.
<div id="box_wrap">
<div class="box_title">
Title
</div>
<div class="box_image">
Image
</div>
<div class="box_content">
Content
</div>
<div class="box_more">
Read More
</div>
</div>
All of this is in a container provided by the CMS itself which has the attibute display: table-cell.
#box_wrap {
display: flex;
flex-direction: column;
}
.box_content {
display: flex;
flex: 2;
}
I think the problem might be that the container provided my the CMS has no defined height. If I give my #box_wrap a fixed height manually then the div in it will work as they should. I also tried height: auto and height: 100% for the #box_wrap and it doesn't work. Again, probably because the parent has no defined height, no? That is the last thing that I need to solve. The #box_wrap needs to stretch over the vertical, currently it only extends as far as it needs to cover the content.
I also noticed that the first image I provided wasn't 100% accurate so I updated it.
I would use this to allow the .box_content to grow (i.e. become higher) and the others not:
.box_title,
.box_image,
.box_image {
flex-grow: 0;
}
.box_content {
flex-grow: 1;
}
In addition, you should apply height: 100% to #box_wrap, but for that you also need height: 100% on body and html to have a reference for the height of #box_wrap. So, to sum up:
html, body {
height: 100%;
margin: 0;
}
#box_wrap {
height: 100%;
}
You also might want to add...
body {
padding: 10px;
box-sizing: border-box;
}
...to get that distance between the edge of the screen and your container as it's shown in your image.

Flexbox not wrapping items as expected

I need to create a drop down navigation menu which wraps onto two lines when it is really long.
By setting the following CSS properties on the menu I can achieve the desired result.
.dynamic {
position: absolute;
max-height: 80px;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
This, however, does not work on Internet Explorer 11. The items do not wrap onto the next line as it does on Chrome.
Here is a jsFiddle
It will work if I use height: 80px instead of max-height: 80px; but that does not work for me as I want the menu to grow with the items.
Does anyone know how I can get IE to wrap the items properly?
Since CSS Flexbox is not fully supported to provided a wrapping menu when the items pass a maximum height I have created a workaround using the height attribute.
As #Michael_B pointed out, the container doesn't wrap around the wrapped items.
A solution to this is apply the background style to the flex items instead of the container like this
This allows the container 'to appear' to grow with the items. Then using the nth-child pseudos I can allow the last item to span the full height of the container.
.dynamic > li:nth-child(4n),
.dynamic > li:last-child:nth-child(n+4) {
flex: 1 0 auto;
}

How do I y-center an image ON CLIP but let it stay top aligned if it is smaller than its container?

I have a container that holds an image. The image fills the container horizontally and the container responds to page size. See fiddle:
im html
<div id="container">
<img src="http://silberschauer.de/img/pre/045.jpg" />
</div>
in css
#container {position:absolute;top:1em;left:1em;bottom:2em;
width:30%;background:#0f0;overflow:hidden;}
#container img {width:100%;}
https://jsfiddle.net/t4um60k1/8/
Is it possible to have the image clip top and bottom equally if (and just if) the container gets smaller in y-axis than the image with css?
EDIT:
You did not understand the question if your solution provides a green area ABOVE the image in any circumstances.
Is it possible to have the image clip top and bottom equally while staying top-aligned if the container gets smaller in v-axis than the image with css?
Yes, this can be done neatly and efficiently with CSS. Flexbox is well-suited for this sort of thing.
No need for JS. No changes to the HTML.
Add this to your CSS:
#container {
display: flex; /* new */
flex-direction: column; /* new */
justify-content: space-between; /* new */
position: absolute;
top: 1em;
left: 1em;
bottom: 2em;
width: 30%;
background: #0f0;
overflow: hidden;
}
#container::before, #container::after {
content: '';
}
#container img {
width: 100%;
flex-shrink: 0;
}
https://jsfiddle.net/t4um60k1/10/
Explanation
We use flexbox to align the image on a vertical axis (flex-direction: column).
Then we tell the flex container that any child elements (aka, flex items) will align as space-between.
With justify-content: space-between, flex items are evenly spaced, with the first item aligned at one edge of the container and the last item aligned at the opposite edge. In this case, because we're in column direction, we're talking about the top and bottom edges.
To ensure that the image stays in the middle, we create "phantom" flex items on each end using pseudo-elements.
But why take these extra steps to center the image, when justify-content: center would have done the job with less hassle?
Because the second requirement of the question is that the image must be top-aligned when it exceeds the height of the container.
With justify-content: center the image will stay centered at all times.
But under the rules of space-between, if flex items overflow the container, space-between switches to flex-start, which aligns the image to the top (more details below).
Lastly, as flex items are generally flexible, flex-shrink: 0 is applied to the image to ensure it doesn't shrink from its original size.
Switching from space-between to flex-start
From the spec:
space-between
Flex items are evenly distributed in the line. If the leftover
free-space is negative or there is only a single flex item on the
line, this value is identical to flex-start.
center
Flex items are packed toward the center of the line. ... If the leftover
free-space is negative, the flex items will overflow equally in both
directions.
(emphasis mine)
source: https://www.w3.org/TR/css-flexbox-1/#justify-content-property
Note that flexbox is supported by all major browsers, except IE 8 & 9. Some recent browser versions, such as Safari 8 and IE10, require vendor prefixes. For a quick way to add all the prefixes you need, post your CSS in the left panel here: Autoprefixer.
You can simply add some css tricks to your image. It's not the modern way, but it works.
#container img {
width:100%;
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
jsfiddle
When the container gets smaller than the image in v-axis, the image is align-top.

How to center things - display:block/inline-block

When centering things in html and css, I find two approaches - either applying on the element :
display:block;
margin:0 auto;
or using:
display:inline-block;
text-align:center; (on the parent element)
I always wonder which is better and why. Thanks!!
This is a classic and important question.
Elements can either be inline (meaning they all sit next to each other - like a span tag) or they can be block (meaning the stack up on top of each other - like a div tag).
Margin: auto, while a bit odd when you first see it, is the best and only way to center a block level (position static), element.
For anything that is display: inline (like a span tag) - the only way to center it is if you specify text-align: center on the parent. This will center everything display: inline inside it that is position: static;
Display: inline-block is a hybrid of the two that is relatively new (but supported as far back as ie7 if you use the hack mentioned in another answer). With inline-block, you get the benefits of inline, in that you can you stick a bunch of things next to each other and have them all be centered (think of a nav where all nav items are all centered), but ALSO have each item take advantage of some of the stuff you get with display: block - the most important one being HEIGHT.
Imagine a scenario where each nav item has a background image that is 80px tall - you can't make an inline element have a height of 80 - so you'd have to make each element display: block - only then can you give it a height. So to make them all appear next to each other, you'd float them. The problem is if you float them, you can't have them all be centered on the page unless you give a fixed width to the nav and margin: auto THAT. This means the nav has a fixed width - might be okay, but there are times when the nav has to have dynamic elements, different widths for different languages, etc.
Enter display: inline-block.
Block elements should always be centered using
.block {
margin-left: auto;
margin-right: auto;
width: 600px;
}
as stated by the w3c: http://www.w3.org/Style/Examples/007/center.en.html#block
text-align: center;
is well-named: use it to center texts.
Update
You can also use display: flex now:
.parent {
display: flex;
justify-content: center;
}
.block {
width: 200px;
}
There is no better way in this situation both approach will work and both are corrected. Just one thing display:inline-block doesn't work on IE7 (if you support this browser) you will need a hack to make it work
display: inline-block;
*display: inline;
zoom: 1;