I have a simple HTML document. I have a header, a section and a div (that contains an unknown number of other divs).
The header and the section do not (and can not) have set heights. Their height comes from the content. Only their width is known (set to 100%).
Is it possible, with flexbox or other means, to get each of those child divs, in this case with class="fill" to be the height of the body - minus the header and section?
In other words, when someone goes to the page, I want them to see the header and the section and then have the first div.fill reach all the way to the bottom, forcing them to scroll to see the next div (but not scroll to see the bottom of the first child div).
I am using a templating system so unfortunately the structure of the HTML can not change and I would like to do this only in CSS.
<html>
<body>
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="container">
<div class="fill">I Want</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</div>
</body>
</html>
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
.container {
flex: 1; /* 1 */
display: flex;
flex-direction: column;
overflow-y: scroll;
}
.fill { flex: 0 0 100%; } /* 2 */
header { background-color: aqua; }
section { background-color: orange; }
.fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<body>
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="container">
<div class="fill">I Want</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</div>
</body>
jsFiddle
Notes:
The flex-grow: 1 component of flex: 1 tells the .container element (a flex item child of body) to consume all remaining space. This will cause .container to use up any space not consumed by header and section.
The flex-basis: 100% component of flex: 0 0 100% tells the .fill items (flex item children of .container) to consume 100% height of the parent. So these items will always take the full height of flex-grow: 1 on the parent.
Because flex items are set, by default, to shrink in order to not overflow the container, an override is set with flex-shrink: 0 in the flex: 0 0 100% rule. This disables the shrinking feature and allows the items to stay fixed at 100% height. (Otherwise, regardless of the defined height / flex-basis, the items would shrink evenly to prevent an overflow. See demo.)
If you change the structure of the elements a bit you can get it with only css.
Basically add the first .fill element in a container with the header and the section (let's call it first). For the other divs use height: 100vh
body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.fill {
height: 100vh;
overflow: auto;
}
.first {
flex: 1;
}
header { background-color: aqua; }
section { background-color: orange; }
.first, .fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<html>
<body>
<div class="container">
<header> Header content, might contain an image</header>
<section> This is the sub header, unknown height </section>
<div class="first">I Want</div>
</div>
<div class="fill">Each of These</div>
<div class="fill">To be </div>
<div class="fill">The height of the body - the Header - the Section</div>
</body>
</html>
Related
In my site I have the following structure:
Header
Content
Footer
And I want to make the Header and the Footer size based on their content (not a fixed size). And the Content to fill the remaining space.
I saw many questions and answers like: Make a div fill the height of the remaining screen space
that solves similar cases but in my case, the Header and Footer sizes are unknown so I can't use the calc() function, and the Header Has position:fixed which removes it from the layout calculations and makes the
flex solutions of various kinds wrong:
html,
body {
height: 100%;
margin: 0;
}
.box {
display: flex;
flex-flow: column;
height: 100%;
}
.box .row {
border: 1px dotted grey;
}
.box .row.header {
flex: 0 1 auto;
position: fixed;
/* The above is shorthand for:
flex-grow: 0,
flex-shrink: 1,
flex-basis: auto
*/
}
.box .row.content {
flex: 1 1 auto;
}
.box .row.footer {
flex: 0 1 40px;
}
<!-- Source - https://stackoverflow.com/a/24979148-->
<div class="box">
<div class="row header">
<p><b>header</b>
<br />
<br />(sized to content)</p>
</div>
<div class="row content">
<p>
<b>content</b>
(fills remaining space)
</p>
</div>
<div class="row footer">
<p><b>footer</b> (fixed height)</p>
</div>
</div>
Or using this solution:
html,
body {
height: 100%;
}
body {
display: flex;
flex-direction: column;
}
.header{
position:fixed;
}
.content {
flex-grow: 1;
border: 1px dotted red;
}
<!-- Source - https://stackoverflow.com/a/28771764-->
<body>
<div class="header">header</div>
<div class="content"></div>
</body>
Is there any way to do make the Content height = 100% - FooterHeight - HeaderHeight
When the Footer and Header dimensions are unknown, and the Header has fixed position?
Since the header is fixed, I think you would need to know its height through JavaScript, and set the body's min-height as 100% of the viewport's height minus the header's height. After, you could simply use CSS Grid on body, to have the content take all the avaiblable height. Like so:
document.body.style.minHeight=`calc(100vh - ${document.querySelector("header").clientHeight}px)`;
document.body.style.paddingTop= document.querySelector("header").clientHeight + "px";
body{
margin:0;
display:grid;
grid-template-rows:1fr auto;
}
header{
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
}
div{
background:lightgreen;
}
footer{
background:lightyellow;
}
<header>I'm the header</header>
<div>I'm the content</div>
<footer>I'm the footer</footer>
I can think of two solutions based on the rather general description of your problem:
A) Use JavaScript to do the calculations for you and apply the values to margins or positions, whichever works better in your case;
B) You could repeat the contents of the header (and footer, id that's out of the document flow also) in element(s) atop the content and make it transparent and non-inter-active (pointer-events: none) - dirty, but if JS is not an option and your header does not offer some other way to determine it's height through some 'css-magic' it might be the only solution.
Quite often I find, that there are better solutions when the problem is more specifically described, so if you can tell us what elements make it impossible to know the height of the header, there might be better solutions. Often when ratios as with images are in play, vh can come to the rescue - even though tha can be tricky too...
Finally I found a pure css solution.
since the Header is in the top , using position: sticky instead of fixed will have the same result but the layout will take it into account when calculating:
html,
body {
height: 100%;
margin:0;
}
body {
display: flex;
flex-direction: column;
}
.header{
position:sticky;
}
.content {
flex-grow: 1;
border: 1px dotted red;
}
<!-- Source - https://stackoverflow.com/a/28771764-->
<body>
<div class="header">header</div>
<div class="content"></div>
</body>
I'm trying to lay out a web page that has three reasons - left, top right and bottom right. The left and bottom right regions should have scrollbars in them, and the entire page should fill the screen. I'm using Bootstrap 4.
I can get the scrollbars working properly around the left region. The problem is with the right regions - the horizontal scrollbar appears on the bottom-right region, as it should, but the vertical scrollbar appears on the entire page. Note that the bottom-right also has a vertical scroll bar, but it's disabled.
#outer {
height: 100vh;
overflow: none;
}
#left-col {
height: 100vh;
overflow: scroll;
background-color: #ff85d4;
}
#left-large {
height: 5000px;
width: 5000px;
}
#right-col {
height: 100vh;
}
#right-top {
background-color: #abff64;
}
#right-bottom {
overflow: scroll;
background-color: #ccddff;
}
#right-bottom-inner {
width: 2000px;
height: 2000px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container-fluid">
<div class="row" id="outer">
<div class="col-9" id="left-col">
<div id="left-large">
Large left
</div>
</div>
<div class="col-3" id="right-col">
<div id="right-top">
<p>
Top 1
</p>
<p>
Top 2
</p>
<p>
Top 3
</p>
</div>
<div id="right-bottom">
<div id="right-bottom-inner">
Right bottom inner
</div>
</div>
</div>
</div>
</div>
How can I make the bottom-right region have its own scrollbars?
Like I said in the comments this is another way to do this with CSS grid which seems like the perfect tool for something this ... "gridy" :)
I gave all the boxes plenty of space you can scroll... adjust at will.
Update: I made the right-top box max-content and grow for as long as the content is while giving the right-bottom box a min height of 20px.
body {
padding: 0;
margin: 0;
}
.wrapper {
display: grid;
grid-template-columns: 1.6fr 0.4fr;
grid-template-rows: max-content minmax(20px, 1fr);
gap: 1rem;
height: 100vh;
width: 100vw;
}
.wrapper-inner {
height: 2000px;
width: 2000px;
}
.left,
.right-top,
.right-bottom {
overflow: auto;
padding: 1rem;
}
.left {
grid-row-start: 1;
grid-row-end: 3;
background: hotpink;
}
.right-top {
background: lime;
}
.right-bottom {
background: skyblue;
}
<div class="wrapper">
<div class="left">
<div class="wrapper-inner">
Large left
</div>
</div>
<div class="right-top">
<p>
Top 1
</p>
<p>
Top 2
</p>
<p>
Top 3
</p>
</div>
<div class="right-bottom">
<div class="wrapper-inner">
Right bottom inner
</div>
</div>
</div>
If you want to force a scrollbar on any block element, you'll need to set a fixed height and include an overflow-x:scroll or overflow-y:scroll property depending on where you want the scrollbar to appear.
If you want a horizontal scrollbar, use overflow-x:scroll;, if you want a vertical scrollbar, use overflow-y:scroll;
flex is magic
CSS
/*Allow children to auto fixed height*/
#right-col {
display: flex;
flex-direction: column;
}
#right-top {
height: fit-content; /*Only use essential*/
}
#right-bottom {
overflow: scroll; /*See child content*/
height:100%; /*use all remain height*/
}
Seeing that the height needs to be explicit (either as percentage or a fixed number), I couldn't find a CSS-only solution.
Instead I used a ResizeObserver to track changes to the size of the right-col and right-top elements (in my actual problem, the right-top element changes in size), calculated the right-bottom element (right-col.height - right-top.height basically), and applied it as a dynamic style on the right-bottom element.
Not pretty but it's working.
I tried using a CSS grid, too (instead of bootstrap altogether), but without explicitly specifying the height of the right bottom element, the scrollbars misbehaved there, as well.
This question already has answers here:
Make a div fill the height of the remaining screen space
(41 answers)
Percentage Height HTML 5/CSS
(7 answers)
Flexbox fill available space vertically
(2 answers)
Closed 3 years ago.
Consider the following code:
.container {
display: flex;
flex-direction: column;
}
.header {
flex: 1;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
height: 100%;
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Why is my CONTENT not getting full available height (from HEADER bottom to the bottom of page) ? How to solve it ?
I'm putting this answer to clear up a few things mentioned in the comments, if it's not appropiate due to the question already having an answer I'll delete this.
By making the changes I proposed, we set the .container's height to 100vh, to explicitly define that it must have the full viewport's height, without this, the .container only has the needed height to contain the elements inside of it.
This applies the same to the body and html elements.
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
flex: 1;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Using percentages to define a height or width require some reference to calculate how much space that % unit is; so for example:
If we set a width of 1000px for .container, we can set its children's width to say, 50% and 100% and they will resize accordingly to 500px and 1000px because they have the 1000px reference from their parent.
EDIT: As noted by #Temani, this reference is always present for the width property, so using percentages for width will never fail, even if we don't specify an explicit width in a parent container.
.container {
display: flex;
flex-direction: column;
width: 1000px;
}
.header {
flex: 1;
width: 50%;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
width: 100%
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
The same happens with the height property; we define a specific
height for the parent, and the children's height can be set with percentages since now they have a reference.
.container {
display: flex;
flex-direction: column;
height: 500px;
}
.header {
height: 20%;
background-color: red;
}
.content {
background-color: blue;
height: 80%
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Your container is missing an height , you can use height:100vh; to fill window's height.
You can also use % , but you need to inherit a valid value from a parent. In this case, it can be take from html, send to body, and finally used by your container:(example in this duplicate)
html,body,.container {height:100%;}
example with vh
body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
/*or min-height*/ height: 100vh;
}
.header {
/* flex: 1; not needed */
background-color: red;
}
.content {
flex: 1;
background-color: blue;
/*height: 100%; not needed */
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Its going to depend on what you want to ultimately do the page and how you are going to use the page.
You can set your .container full page width & height:
.container {
display: flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
and then grow/shrink your containers as needed:
.header{ flex: 1 }
.content { flex: 2 } // twice as large as header
How #ivanS95 says '.container only has a height based on its content'.
Instead, you can do this by setting all parents (html, body) elements to 100% also the .container at 100% too, and changing your flex propierty of .header not allowing it to grow.
Example here:
flex: 0 1;
https://codepen.io/pen/
This question was very nicely answered before by #Pebbl at:
Make a div fill the height of the remaining screen space
please check it.
I have a following layout fully working in Firefox and IE:
Unfortunately it is quite broken in Chrome, namely the dark blue container is collapsed even though it has height 100% of its parent:
I tried this approach, but without any luck. Any ideas how to fix this on Chrome without breaking it in other browsers?
html,
body {
height: 97%;
margin: 0;
padding: 0;
}
div {
border: 10px dotted teal;
}
.container {
display: flex;
border-color: tomato;
height: 100%;
}
.row {
flex-flow: row;
}
.column {
flex-flow: column;
}
.item1 {
flex: 1;
}
.item2 {
flex: 2;
}
.item3 {
flex: 3;
}
.c1 {
border-color: gold;
}
.c2 {
border-color: darkblue;
}
<div class="container">
<div class="item3">
<div class="container column c2">
<div class="item1 c1"></div>
<div class="item3"></div>
</div>
</div>
<div class="item1 c1"></div>
<div class="item2"></div>
</div>
The question says:
I have a following layout fully working in Firefox and IE.
Unfortunately it is broken in Chrome, namely the dark blue
container is collapsed even though it has height 100% of its parent.
Actually, an argument could be made that the opposite is true: Chrome has it right, while Firefox and IE are "broken".
First, here's the solution:
.item3 { height: 100%; }
Now let's look at your document structure and the heights applied:
<html> <!-- height: 97% -->
<body> <!-- height: 97% -->
<div class="container"> <!-- height: 100%; -->
<div class="item3"> <!-- height: ?? -->
<div class="container column c2"> <!-- height: 100% ; this is the collapsed box -->
...
...
...
As per the CSS specification, when using percentages to set the height of an element (like you are doing with .container), the parent element must also have an explicit height. In reference to your collapsed div, its parent (.item3) does not have a defined height.
From the spec:
<percentage>The percentage is calculated with respect to the height
of the generated box's containing block. If the height of the
containing block is not specified explicitly (i.e., it depends on
content height), and this element is not absolutely positioned, the
value computes to 'auto'.
autoThe height depends on the values of other properties.
In terms of the height property, it would appear from this example that Chrome defines "containing block" as "parent", while Firefox and IE define "containing block" as "ancestor", or they respect flex heights as a reference for percentage heights.
Hence, since the div with the dark blue border (.container column c2) has no content, and its parent has no specified height, then there is no height and the box collapses in Chrome.
However, by specifying a height for .item3, which is the parent of the collapsed box, the height works on all browsers.
DEMO
UPDATE
More details:
Heights rendering differently in Chrome and Firefox
you missed to mention height of .item3 class in percentage, we should make sure parent should have height in percentage unit then only child height in percentage will work.
html,
body {
height: 97%;
margin: 0;
padding: 0;
}
div {
border: 10px dotted teal;
}
.container {
display: flex;
border-color: tomato;
height: 100%;
}
.row {
flex-flow: row;
}
.column {
flex-flow: column;
}
.item1 {
flex: 1;
}
.item2 {
flex: 2;
}
.item3 {
flex: 3;
height: 100%;
}
.c1 {
border-color: gold;
}
.c2 {
border-color: darkblue;
}
<div class="container">
<div class="item3">
<div class="container column c2">
<div class="item1 c1"></div>
<div class="item3"></div>
</div>
</div>
<div class="item1 c1"></div>
<div class="item2"></div>
</div>
I'm trying to achieve the three-column layout generally described as the "holy grail" (see this ALA article) using the new display: flex syntax.
The requirements are as follows:
A header and footer, with between them three columns
The outer columns have fixed widths
The inner column stretches to fill the space between the side columns, with a minimum and maximum width beyond which it will not stretch (so neither should the container)
The footer should be at the bottom of the viewport, until the content actually pushes it below
I got the first three requirements down with the following code:
<body>
<div class="container">
<header class="masthead">
<h1>The Header</h1>
</header>
<div class="side-left column">
Left sidebar
</div>
<div class="middle column">
Content goes here
</div>
<div class="side-right column">
Right sidebar
</div>
<footer class="footer">
© Footer
</footer>
</div>
</body>
CSS:
.container {
display: flex;
flex-flow: row wrap;
min-width: 500px;
max-width: 1100px;
}
.masthead {
flex: 1 100%;
}
.side-left,
.side-right {
flex: 0 0 150px;
}
.middle {
flex: 1;
}
.footer {
flex: 1 100%;
}
Live in action: jsBin
However, I'm stuck with the 100% height. I already tried setting either some of the columns or the container to height: 100% or min-height: 100% but none seem to work. Do I need one of the many other flex properties to handle this? I can't seem to see the forest through the trees.
.container { min-height: 100vh; }