Unexpected size for flex item child - html

I want to build a column layout with a menu, then a header, then a content container using flexboxes.
I know how to build it in other techs using fixed sizes, calc etc... But have troubles with flexboxes.
Here is a JsFiddle
I have this:
<div class="layout">
<div class="menu">
Menu
</div>
<div class="header">
Header
</div>
<div class="content">
<div class="scrollable-content">
...
</div>
</div>
</div>
.layout {
overflow: hidden;
border: solid;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.menu {
overflow: hidden;
border: solid red;
flex-grow: 0;
flex-shrink: 0;
}
.header {
overflow: hidden;
border: solid green;
flex-grow: 0;
flex-shrink: 0;
}
.content {
overflow: hidden;
border: solid blue;
flex-grow: 1;
flex-shrink: 1;
}
.scrollable-content {
overflow: auto;
height: 100%;
width: 100%;
}
As you can see on the JsFiddle, with this code, the .scrollable-content is actually never scrollable, because even while using height: 100% it becomes much larger than his parent div. How can I constrain that div's height to the parent's height?
Note: I know I could put the overflow: auto to the parent .content directly, but for reasons specific to my app I really don't want to: please only submit solutions that do not modify the html structure or change the scrollable container because I already know these solutions. I'm more interested to learn why my approach did not work and how it could be fixed (or not?)
Obviously the content that can be scrolled has a dynamic height and is not a fixed value of 450px like in my JsFiddle.

You could remove height: 100% from .scrollable-content, and then change the display of .content to flex:
Updated Example
.content {
overflow: hidden;
border: solid blue;
flex-grow: 1;
flex-shrink: 1;
display: flex;
}
.scrollable-content {
overflow: auto;
width: 100%;
}

You're missing a height: 100% on .content.
Since you're using percentage heights, you need to specify a height for all parent elements of the .scrollable-content.
For a more detailed explanation see: Working with the CSS height property and percentage values

You are almost there.
.content {
overflow: hidden;
border: solid blue;
flex-grow: 1;
flex-shrink: 1;
height:450px; //adjust accordingly.
}
The scroll activates once a height a limited height has been reached. In your case since you have the scrollable area limited to the parent's (content) height, you did right by giving it height: 100%....but....100% of what?
So, all you need is give the content class a stopping point. change the 450px to whatever is more suitable. If you are trying to cover for a gap below that content then the next item should be positioned absolute bottom. (just in case that is what you are tying to get to) I've seen this approach many times and that is usually what follows :)
Here is your updated jfiddle

Related

How to use overflow with flex-grow

I have this code:
html,
body,
#container {
width: 100%;
height: 100%;
}
#container {
display: flex;
background: #ddd;
}
#width {
width: 200px;
height: 100%;
resize: horizontal;
overflow: hidden;
background: #eee;
}
#remaining {
flex-grow: 1;
overflow: scroll;
}
#resize {
width: 200px;
height: 200px;
resize: both;
overflow: hidden;
background: #ccc;
}
<!DOCTYPE html>
<html>
<body>
<div id="container">
<div id="width">Width</div>
<div id="remaining">
<div id="resize">Resize</div>
</div>
</div>
</body>
</html>
Here is what I am trying to make:
You can resize #width
You can resize #resize
The issue is that when I resize #resize, #width shrinks to make room for it.
What I am looking for is when I resize, #remaining shows a scroll bar, and doesn't resize anything else.
I believe that this is happening because #remaining doesn't have a width property, but instead flex-grow. And so it allows it to vary in width, because it doesn't have a set width.
I am looking for a pure HTML/CSS answer, but JavaScript would work too. (of course as long as it is reliable, won't break, and doesn't massively slow down execution using methods such as setInterval)
I also do want to say that I am using a flex layout on #container only for the purpose of making #remaining take up all of the remaining space, and if display: flex is removed, it would still work with the full code that I have.
If you abandon the flex:grow and instead calculate the width of remaining in the css it works.
.remaining{
width: calc(100% - 200px);
overflow-x: auto;
}
With a fiddle

Flex 1 : height is not fixed to remaining space

I have a "boxHeader" which height is fixed to 64px and a "boxcontent" which I want height to be fixed to the remaining space.
My problem: if the height of the children in the "boxContent" exceed the height of the remaining space, it will also increase the height of "boxContent". And I don't want that, I want the children of "boxContent" to respect the max size (and add scroll)
Edit: The children inside "boxContent" are React Components, I have a "Sider" of Ant Design containing multiple Panel's Collapse AND I have a Content side by side. I want to add a scroll bar on the Sider AND on the Content so both Sider and Content have overflow-y: auto
Can you help me ?
Here the code
<div id="container">
<div id="boxHeader">
<HeaderArea />
</div>
<div id="boxContent">
{...}
</div>
</div>
#container {
display: flex;
flex-flow: column;
height: 100% !important;
min-height: 100% !important;
#boxContent {
/* height: calc(100vh - 64px); this works, but i don't want to calcule the size*/
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
background: white;
}
#boxHeader {
flex: 0 0 64px;
}
body{
height: 100vh; /*100% is also tested*/
}
You can give #boxContent max-height and overflow-y: scroll with these two I think it is going to work. But If it is not can you add your HTML so that I can look and fix it. I hope this line of code fixes your problem.
#boxContent {
max-height:300px; //(for example)
overflow-y: auto;
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
background: white;
}

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 - scroll issue with flexbox and max-height

I'm facing a strange issue that might have link with flexbox misbehaving with max-height, but so far I didn't find any pure css solution.
I made a plunker to summarize the problem. If you resize your window to reduce its height, at some point you should have a scrollbar in the first block, but if you get back to a higher height, even if there is enough space, the scrollbar won't disappear unless you put your mouse over it (which feels very bugy) : https://plnkr.co/edit/VsJ7Aw8qZdSM1iJeL7Bj?p=preview
I have a main container (in flex) containing 2 blocks (also in flex).
The main container has its height set to 100%, allowing it to resize itself following the window size.
Both children have a fixed content and an overflow-y set to auto.
The first child has a max-height in % to let more height to the second child.
The issue seems to come from this max-height rule. If you remove it, then there's no problem, but I need this max-height...
I don't want to use something like:
.max { flex: 1 1 auto; }
.all { flex: 3 1 auto; }
because it would make my first block higher than its content depending on the window size. I want the first block to have at most its content height.
So my question is: Is it an implementation issue in many browsers (maybe all, but I only tested it in Chrome, IE10 and IE11), or is something wrong in my logic ?
Thank you.
UPDATE: I used a fixed height for my content in this example, but in my project it's a list of n elements in it. So I can't really set my max-height with px value.
UPDATE2: I can't use vh in .max max-height property because it takes 100vh as 100% of viewport height (basically your browser window height). But in my context, .main is already in other containers. Those containers have already their heights defined and are smaller than my window height.
/* Styles go here */
html {
height: 100%;
}
body {
height: calc(100% - 16px);
}
.main {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.max,
.all {
display: flex;
flex-direction: column;
width: 100%;
overflow-y: auto;
}
.max {
flex: 0 1 auto;
min-height: 103px;
max-height: 40%;
background-color: green;
}
.all {
flex: 2 1 auto;
min-height: 235px;
background-color: blue;
}
.content {
flex: 0 0 auto;
box-sizing: border-box;
height: 200px;
margin: 5px;
border: 1px dashed black;
background-color: white;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="main">
<div class="max">
<div class="content"></div>
</div>
<div class="all">
<div class="content"></div>
</div>
</div>
</body>
</html>
It is a bug, in Chrome, a test in FF and Edge, it works fine.
Since you use full viewport height, change the max-height: 40%; to max-height: 40vh;.
Another way, as in below sample, is to change the 100% in height: 100% to 100vh.
I guess this works better because viewport units like vh is a fixed unit, which percent is not.
Plnkr demo: https://plnkr.co/edit/66W4a2lOI58XLudCmkw9?p=preview
html {
height: 100vh;
}
body {
height: calc(100vh - 16px);
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
}
.max,
.all {
display: flex;
flex-direction: column;
width: 100%;
overflow-y: auto;
}
.max {
flex: 0 1 auto;
min-height: 103px;
max-height: 40%;
background-color: green;
}
.all {
flex: 1 1 auto;
min-height: 235px;
background-color: blue;
}
.content {
flex: 0 0 auto;
box-sizing: border-box;
height: 200px;
margin: 5px;
border: 1px dashed black;
background-color: white;
}
<div class="main">
<div class="max">
<div class="content"></div>
</div>
<div class="all">
<div class="content"></div>
</div>
</div>
Yes it feels buggy. If you increase the height of the window the height of the first box does not get updated unless:
you decrease the height again
"put your mouse over it" (did not quite get your meaning here)
IMHO this is a browser bug.
If you set flex-grow to anything greater 0 for the first box, the height gets updated correctly, if you increase the height of the window (as you would expect) But using flex-grow isn't an option as the box could potentially grow bigger than its content.
Rather than using max-height:40% you should use the exact same height as you use for .content and use flex-grow: 1 as well to circumvent the "browser bug"

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;
}