How to use overflow with flex-grow - html

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

Related

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"

Unexpected size for flex item child

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

How to add vertical scroll bar to html page without using div?

I am generating one html page having one tab pane which is very long. So i want to add scroll pane so that visualisation could be better. I found few good examples using div but code becomes very messy with other div so i prefer not to use div.
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>Independent CSS scrolling panels (with inertia)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="Top">Top Content</div>
<div class="Container">
<div class="Left">Left Content</div>
<div class="Middle">Middle Content</div>
<div class="Right">Right Content</div>
</div>
</body>
</html>
CSS code:
/*I love me some border-box*/
* {
box-sizing: border-box;
}
/*This just stops me getting horizontal scrolling if anything overflows the width*/
body {
overflow-x: hidden;
}
/*Just removing default browser padding/margin*/
html,
body {
padding: 0;
margin: 0;
color: #ebebeb;
}
/*Flexbox gives us the flexiness we need. The top just stays put as there is no scrolling on the body due to the page never exceeding viewport height*/
.Top {
display: flex;
align-items: center;
justify-content: center;
background-color: darkgreen;
font-size: 3rem;
position: relative;
z-index: 10;
height: 100px;
}
/*This is our main wrapping element, it's made 100vh high to ensure it is always the correct size and then moved into place and padded with negative margin and padding*/
.Container {
display: flex;
overflow: hidden;
height: 100vh;
margin-top: -100px;
padding-top: 100px;
position: relative;
width: 100%;
backface-visibility: hidden;
will-change: overflow;
}
/*All the scrollable sections should overflow and be whatever height they need to be. As they are flex-items (due to being inside a flex container) they could be made to stretch full height at all times if needed.
WebKit inertia scrolling is being added here for any present/future devices that are able to make use of it.
*/
.Left,
.Middle,
.Right {
overflow: auto;
height: auto;
padding: .5rem;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: none;
}
/*Entirely optional – just wanted to remove the scrollbar on WebKit browsers as I find them ugly*/
.Left::-webkit-scrollbar,
.Middle::-webkit-scrollbar,
.Right::-webkit-scrollbar {
display: none;
}
/* Left and Right are set sizes while the Middle is set to flex one so it occupies all remaining space. This could be set as a width too if prefereable, perhaps using calc.*/
.Left {
width: 12.5rem;
background-color: indigo;
}
.Middle {
flex: 1;
}
.Right {
width: 12.5rem;
background-color: violet;
}
Can pls someone pls help me how can i implement it.
I suppose you could use a <table>:
table {
display: block;
height: 500px;
overflow-y: scroll;
}

No padding when using overflow: auto

I can’t get padding-bottom to work when I use overflow-y: auto on a box. I use Firefox.
#container {
padding: 3em;
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
background: red;
}
#some_info {
height: 900px;
background: #000;
}
<div id="container">
<div id="some_info"></div>
</div>
See the JSFiddle.
One more solution without extra DIVs.
#container:after {
content: "";
display: block;
height: 50px;
width: 100%;
}
Working in FF, Chrome, IE8-10.
I'm late to the party, but I thought it was worth adding a different solution that addresses some of the concerns raised above.
I came here because of exactly the kind of situation that #Philip raised in response to Alexandre Lavoie's solution: I have dynamically generated content inside the container, so I can't just apply styling to a specific div name like #some_info.
Happily, there's a simple solution for browsers that support CSS3: instead of applying bottom padding to the container, apply a bottom margin to the last child element inside the container.
#container > :last-child {
margin-bottom: 3em;
}
As long as the last child element in the container div is a block-level element, this should do the trick.
DEMO: http://jsfiddle.net/rwgZu/240/
P.S. If Firefox's failure to scroll to the bottom of the padding is indeed a bug (as suggested by #Kyle), it still hasn't been fixed as of Firefox 47.0. Frustrating! Internet Explorer 11.0.9600.17843 exhibits the same behavior. (Google Chrome, in contrast, shows the bottom padding as expected.)
The solutions above were not working for my needs, and I think I stumbled on a simple solution.
If your container and overflowing content share the same background color, you can add a top and bottom border with the color matching the background color. To create equal padding all around, set the border width equal to the left and right padding of the container.
Link to modified version of OP's fiddle: http://jsfiddle.net/dennisoneil/rwgZu/508/
A simple example below.
Note: Stack Overflow puts the snippet results into an overflow scroll, which makes it a little harder to see what's going on. The fiddle may be your best preview option.
#container {
background: #ccc;
overflow-y: scroll;
height: 190px;
padding: 0 20px;
border-top: 20px solid #ccc;
border-bottom: 20px solid #ccc;
}
#overflowing {
background: #ccc;
}
<div id="container">
<div id="overflowing">
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
This is content<br/>
</div>
</div>
Here is a possible approach that is working perfectly :
#container {
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
}
#some_info {
height: 900px;
background: #000;
border: 3em solid red;
}
Style the parent div normally and make the inner div do what you want it to do.
Remove overflow-x and overflow on #container, change height to 100% and add overflow-y:scroll; on #some_info
#container {
padding: 3em;
width: 300px;
height: 300px;
background: red;
}
#some_info {
height: 100%;
background: #000;
overflow-y:scroll;
width:100%;
}
Working Demo: http://jsfiddle.net/9yuohxuh/
For those who are looking for a simple solution and can change the DOM, put the overflow on the outer element and the padding on the inner element.
.scroll {
overflow-x: hidden;
overflow-y: auto;
}
.scroll__inner {
padding: 3em;
}
In the example from the original question, it would look like this:
#container {
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
background: red;
}
#some_info {
height: 900px;
background: #000;
padding: 3em;
box-sizing: border-box; /* only needed if wanting padding to not be added to height */
}
Note the use of box-sizing: border-box here, which is only needed as the OP has a hardcoded height (generally bad practice but could be needed in edge cases), so adding this border-box enables the 3em padding to not increase the height, but pad inside the 900px.
A final note, I'd advise avoiding ID's for styling, mostly due to their extremely high specificity, see this post for more info on that.
Demo
Hi now used to this css
#container {
padding: 3em;
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
background: red;
padding-bottom:0; // add this line in your css
}
#some_info {
height: 900px;
background: #000;
margin-bottom:3em; // add this line in your css
}
Demo
It's not only with bottom padding. Right padding/border/spacing is also ignored (you can't see it in your example because it has no content, and the width is not scrolling)
All the answers above fail in chrome 43, generating up to 3 scrollbars! or if the content overflows #some_info.
Example: http://jsfiddle.net/LwujL3ad/
If it worked for you, it's probably because the content was not as wide as the scrolling element, or fixed sized.
The right solution is:
Set #some info to display:table, and add padding or border to it, not to the scrolling container.
#container {
overflow: scroll;
width: 300px;
height: 300px;
background: red;
padding-bottom:0;
}
#some_info {
display:table;
border: solid 3em red;
height: 900px;
background: #000;
margin-bottom:3em;
color: white;
}
DEMO: http://jsfiddle.net/juh7802x/
The only element that doesn't fail, and respects ANY border and padding you add in there as separator is a TABLE.
I tried, and no matter if it's the next direct child or it's nested many items deep, any non-content styling will NOT expand to wrap the content, and will stay 100% width of the parent. Which is nonsense, because having content BIGGER than the parent is EXACTLY the scenario in which a scrolling div is required!
For a dynamic solution (both the container and the content) set the container of the elements inside the scrolling container to display:table.
Based on isHristov's answer:
#container {
padding: 3em 3em 0 3em; /* padding-bottom: 0 */
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
background: red;
}
#container:after {
content: "";
display: block;
height: 0;
width: 100%;
margin-bottom: 3em; /* length you wanted on padding-bottom */
}
However, his solution adds extra space in browsers that handle this situation properly.
Dan Robinson's answer is great too unless you have multiple elements, in #container, that are dynamically shown/hidden. In that case :last-child might target a hidden element and have no effect.
You just need to add box-sizing: border-box to the same element where you applied the overflow rule.
I think #-moz-document url-prefix() is what you need.
#container {
padding: 3em;
overflow-x: hidden;
overflow-y: auto;
width: 300px;
height: 300px;
background: red;
}
#some_info {
height: 900px;
background: #000;
}
#-moz-document url-prefix() {
#container > :last-child {
margin-bottom: 3em;
}
}
<div id="container">
<div id="some_info"></div>
</div>
The top answers did not work in FireFox 89. The only sensible solution I could think of is to use a div containing only a non-breaking space and with a fixed height set.
HTML
<div className="spacer"> </div>
CSS
.spacer {
height: 30px;
}
This works as it does not utilize margin or padding.
I have just faced this issue, it persists even in Firefox 87, version being released in 2021.
But it is finally fixed very recently. After update to Firefox 93 bottom padding with scroll works normally.

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