flexbox parent smaller than child elements on browser resize [duplicate] - html

This question already has answers here:
Why don't flex items shrink past content size?
(5 answers)
Closed 5 years ago.
Why is the parent smaller than the children on narrow screens?
See snippet... then resize your browser (width-wise) to be smaller than the pink box. The scroll bars should appear. Scroll back to the right on the page and note the green background is smaller than the pink area and there is a white spot on the right.
So few questions:
Why does it happen?
How do I prevent the parent div's green background from getting smaller than the pink box/div when the browser is resized without setting an explicit width on the parent (or anywhere else) or using overflow:hidden?
Is there a flexbox solution to this problem?
Thanks,
Thomas
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>

Flex items, by default, cannot be smaller than the size of their content. Therefore, while the parent can shrink, the flex items cannot shrink past the length of the text. This causes the overflow and, as a result, the column of white space below.
The initial setting on flex items is min-width: auto. In order for each item to stay within the container, switch to min-width: 0.
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
min-width: 0; /* NEW */
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
Now the pink boxes are not overflowing the container anymore.
However, the text is now overflowing the pink boxes.
The question doesn't specify behavior for the text, but here's one possible solution:
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
min-width: 0; /* NEW */
text-overflow: ellipsis; /* NEW */
white-space: nowrap; /* NEW */
overflow: hidden; /* NEW */
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>

It happens, because your .parent is a normal block element (flex is also a block-level container) and that is initialized with width:auto;, which is in your case the width of the viewport. So scrolling to the right will show white space because your div's width is smaller than the whole scrollable area.
You do that with setting the .parent to an inline element, which will respond to its childrens width.
Yes, just use display: inline-flex; on the .parent.
.parent {
height: 400px;
width: 100%;
background-color: green;
display: inline-flex;
}
.item {
flex: 1;
height: 100px;
background-color: pink;
padding: 10px;
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
See display on MDN.

Related

Children of overflowing flex container exceed container [duplicate]

This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 4 years ago.
I've been on this for a while now and tried a lot of the solutions I've seen across different Stackoverflow questions / blogposts / ... But I honestly can't figure out what's going wrong.
I've got a flexed div with two divs in there. The top div A has a fixed height, the other div B fills the rest using flex: 1;. If the screen is resized and it's smaller than the height of A + B together, then B will start overflowing. I want it to scroll, but I also want the content to be fully visible when scrolling. For some reason which I can't figure out, the content renders out of the top of div B as you can see in this screenshot of my fiddle:
Some of the previously asked questions got me somewhere. For example setting the body to height: auto;, but then when my screen is bigger than A + B it can't be center aligned anymore. min-height: 0; also doesn't seem to help in this case.
How can I make sure my container overflows but will fully show the content of it?
You can solve the issue by giving .second:
flex-basis: auto;
flex-shrink: 0;
Or, with shorthand: flex: 1 0 auto;
Working example:
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
body {
display: flex;
flex-direction: column;
}
.second {
flex: 1 0 auto;
background: blue;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
min-height: 0;
overflow: auto;
}
.container {
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
min-height: 0;
/* added this to make it obvious. Obviously, not needed */
padding: 2rem 0;
}
.container-child {
height: 110px;
background: green;
width: 100%;
}
.container-child:not(:last-child) {
margin-bottom: 30px;
}
<div class="second">
<div class="container">
<div class="container-child"></div>
<div class="container-child"></div>
<div class="container-child"></div>
</div>
</div>
I added some top and bottom padding to .container to make it obvious that it's working - but it's not needed.
Now let's look at why this is happening. When you apply .second { flex:1; } it means:
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
... which allows it to have a smaller size than its contents.
Whenever you have a bigger child centered in a smaller parent, the browser won't provide a scrollbar to top (or to left, when horizontal), because then , if the top of the parent and the top of the child coincide and the child is bigger than the parent, the child is no longer centered, is it?
The same happens when using other centering techniques and you center a bigger child in a smaller parent.
To fix the problem, you need to prevent the child from outgrowing the parent.
In this case, it meant sizing .second based from its content (flex-basis: auto) and not allowing it to shrink: (flex-shrink: 0;).
To better visualize the issue, consider this example:
.left, .right {
position: relative;
border: 1px solid red;
padding: 1rem 5rem;
}
.left {
left: -5rem;
}
.right {
right: -5rem;
}
<div class="left">
I'm taken left
</div>
<div class="right">
I'm taken right
</div>
If the browser provided scrollbars to allow you to scroll to beginning of .left, it would mean that left: -5rem did not apply. I hope that makes sense, I can't explain it better.

CSS flexbox overflow 100% width elements

On mouse over, I want to transform the position of two flex element that are sitting next to each other as per the image below
The markup is as follows
<div class="container">
<div class="element">Normal</div>
<div class="element">Hover</div>
</div>
I want both elements to be 100% width of the parent and the second element to overflow so I can transform X on mouse over. The problem I'm having is both elements get squeezed in to the container.
I know I can wrap the two elements in another div and give it 200% width of container. But want to know if this can be done with flexbox
.container {
display: flex;
width: 200px;
overflow: hidden; /* hides the flex item parked outside the container */
cursor: pointer;
border: 1px solid black;
}
.element {
height: 100px;
flex: 0 0 100%; /* can't grow, can't shrink, fixed an 100% width */
transition: .5s;
min-width: 0; /* override min-width: auto default;
https://stackoverflow.com/q/36247140/3597276 */
}
.container:hover > .element:first-child {
flex-basis: 0;
}
.element:first-child { background-color: lightgreen; }
.element:last-child { background-color: orange; }
<div class="container">
<div class="element">Normal</div>
<div class="element">Hover</div>
</div>
You wrote:
The problem I'm having is both elements get squeezed in to the container.
That was probably because flex items are set, by default, to flex-shrink: 1, meaning they are permitted to shrink in order to fit inside the container. In my answer flex-shrink is set to 0.

column flex with height 0 padding trick

I have 2 divs that are placed on top of each other. For purposes of alignment, I am using display: flex and flex: column on the div containing these two divs. However, the first div uses the "height 0 padding" trick for videos. The problem I'm having, is that when using flex: column and change the width of the screen, the height doesn't change (and I want the height to change so that it matches the ratio for the video). What ends up happening is that the div stays the same, and the video shrinks within it and it looks ugly because there is extra background.
Plunker: https://plnkr.co/edit/TaeF5f8VufJWPU3GRZPr?p=preview
(in short, I want it such that when I change the width of the browser, the red div's height gets smaller)
CSS
/* Styles go here */
body {
height: 100%;
}
.container {
height: 80vh;
display: flex;
flex-flow: column;
}
.video {
flex: none;
height: 0;
padding-bottom: 30%;
background-color: red;
width: 80%;
}
.next-content {
flex: 1 0 auto;
width: 80%;
background-color: blue;
}
HTML:
<body>
<h1>Hello Plunker!</h1>
<div class="container">
<div class="video"></div>
<div class="next-content"></div>
</div>

How to align multiple divs left and right in the same line in a responsive container (% width)?

I wanted the behaviour of inline-block and float together of several divs in a responsive container (% width), where:
float effect:
1a. it would have divs left and right aligned
1b. when the window width shrinks - the space between the left and right divs would also shrink
inline-block effect:
2a. all the divs would be in the same line
2b. when the window width shrinks - it will hide the divs (usually starting on the right)
Examples:
when the window is larger that the divs:
when the window is smaller that the divs:
2a. the wanted effect
2b. the wrong effect
Here is a code example of what I got so far (which only simulates the float effect (1) and effect in same line (2b) but missing effect when shrinking the window width (3b) ), with a demo:
.item1 {background-color: red;}
.item2 {background-color: yellow;}
.item3 {background-color: blue;}
#container {
width: 95%;
height: 50px; /* not sure if is needed */
}
#container div {
display: inline-block; /* does nothing with floats */
width:100px;
height: 50px;
}
.left {float: left;}
.right {float: right;}
<div id="container">
<div class="item1 left">item1</div>
<div class="item2 right">item2</div>
<div class="item3 right">item3</div>
</div>
Notes:
The container has a % value (to be a bit responsive).
the width of the divs (items) is supposed to have a fixed width (to have a logo image, a menu, and some icons (more images), ...)
in the example, I've also added the inline-block just to show that it does not works with it
the example only uses 3 divs (1 left and 2 right), but if possible I preferred a solution where I could have more without changing too much of code.
(I want to avoid fixed position (like right:20px or 20%) to be easier to add new items.)
was supposed to find a solution using only html and css (or html5 and css3) (avoiding JavaScript).
You should take a look at bootstraps grid system. It would save you a lot of time. http://getbootstrap.com/2.3.2/scaffolding.html#gridSystem
May be it is not fully what you want, but it gets close:
use flex for the container, and hide the second row.
and play with flex-shrink, flex-basis, max-width and margin
.container {
width: 80%;
display: flex;
flex-wrap: wrap;
height: 100px;
margin-bottom: -100px;
border: solid 1px red;
overflow: hidden;
}
.container div {
height: 100px;
border: solid 1px;
}
.left {
flex: 200px 0 0;
background-color: lightgreen;
margin-right: auto;
}
.right {
flex: 100px 1 1;
background-color: yellow;
max-width: 200px;
}
.disappears {
flex: 200px 0 1;
background-color: papayawhip;
}
<div class="container">
<div class="left">left</div>
<div class="right">right</div>
<div class="disappears">dis</div>
<div class="disappears">dis</div>
</div>
You need to add overflow:hidden; to your #container
#container {
width: 95%;
height: 50px; /* not sure if is needed */
overflow: hidden;
}
After more research and tests, I finally come up with a solution, with a demo:
.item1 {background-color: red;}
.item2 {background-color: yellow;}
.item3 {background-color: blue;}
#container {
width: 95%;
display: flex;
justify-content: space-between;
}
/* affects container_left and container_right divs */
#container > div {
display: flex;
}
/* affects items */
#container > div > div {
width: 100px;
min-width: 100px; /* to avoid shrinking in IE 11*/
height: 50px;
}
#container, #container > div {
/* reading from right to left (RTL)*/
/* flex-direction: row-reverse; */
}
<div id="container">
<div class="container_left">
<div class="item1">item1</div>
</div>
<div class="container_right">
<div class="item2">item2</div>
<div class="item3">item3</div>
</div>
</div>
Notes:
It does the effects asked, at least in Firefox (v 37.0.2) and Internet Explorer (IE 11).
In chrome (v 42.0.2311.135 m) it doesn't respect the min-width. Maybe there's some way around it that I didn't realize.
Changed the html to put 2 containers for left and right items, (and if needed you could also add a middle container). And in css using the flex property with the "justify-content: space-between" to align the containers.
New items can be easily added to the right positions (just in html) without the need to use a grid system.
Also added some css code to uncomment if you want to reverse the item order (in right to left languages).

Bottom margin of last child gets hidden when overflow applies

I have a container div which has children anchored to the bottom. The problem is that when the div's overflow scrollbar appears, the bottom margin of the last child gets hidden.
Please see http://jsfiddle.net/TxEAP/3/. At first, there's a correct margin underneath the 1 div. Clicking "append one" so that the scrollbar eventually appears makes the last div not have a bottom margin anymore. Opening DevTools shows that the margin of that last child is there, but it is outside of the container's viewport, even when scrolling completely to the bottom.
How can this be solved? It would suffice to get this working in Google Chrome.
HTML:
<div class="main">
<div class="container">
<div class="item">1</div>
<!-- several of these .item divs -->
</div>
</div>
CSS:
.main {
height: 200px;
overflow-y: scroll;
position: relative;
border: 1px solid black;
}
.container {
width: 100%;
max-height: 100%;
position: absolute;
bottom: 0;
}
.item {
padding: 20px;
margin: 15px;
border: 1px solid black;
}​
Here's my final solution using flexbox. It's supported well enough on Chrome despite all -webkit- prefixes. Basically, the idea is to have a dummy element that, in case of no overflow, fills up the space of the container starting from the top (so that the real children are anchored to the bottom); in case of overflow, it is hidden automatically because of height: 0. It does not suffer from the margin issue, and it does not collapse margins.
http://jsfiddle.net/mCYLm/1/
HTML:
<div class="main">
<div class="gap-filler"></div>
<div class="item">foo</div>
<!-- more `div.item`s -->
</div>
CSS:
div.main {
display: -webkit-box;
-webkit-box-orient: vertical;
height: 200px;
overflow-y: scroll;
}
div.main div.gap-filler {
-webkit-box-flex: 1;
height: 0;
}
div.main div.item {
border: 1px solid black;
margin: 20px;
padding: 20px;
}​
Edit: This was a solution without flexbox, but it had selection issues.
A solution that eventually worked was the following: http://jsfiddle.net/TxEAP/7/. This appends hidden "content" which makes Chrome not hide the margin of the last .item div.
.container:after {
content: "";
font-size: 0;
display: block;
height: 1px;
}
Edit: The following only works if display: inline-block is possible.
Finally I found a solution. If all .items have display: inline-block except the first one, then the margin does not get hidden.
http://jsfiddle.net/TxEAP/5/
.item:not(:first-child) {
display: inline-block;
/* attempt at getting `width: auto` like `display: block` has */
width: -webkit-calc(100% - 2 * 15px);
box-sizing: border-box;
}
If you just move the overflow-y: scroll; from .main. to .container class then the margin is preserved. The only drawback is for less than 3 items (for the given container height) you get a small scrollbar placeholder, instead of a full height one.
Removing max-height:100% on the container seems to fix it for my test in Chrome 21.
Moving the properties so that the overflow is on the container, preserves the margin/padding for an element added to the end that results in the scrollbar appearing.
.main {
height: 200px;
border: 1px solid black;
}
.container {
width: 100%;
height: 100%;
overflow-y: scroll;
}