CSS stretch div vertically to fill remaining container - css

It seems so simple, but I wrap my mind about it and googled a lot but couldn't find an answer:
Container with two vertical rows, one has height defined in pixels (header), and the other has image that should stretch as much as remaining height (slider div). The problem is that this height of header is dynamic (as in unknown) and we can't use that value in defining CSS of container or slider div.
How do I solve it without javascript?
<section>
<header style="height:40px; background: yellow;">header</header>
<div id="slider">
<img src="http://amanita-design.net/img/home-news/botanicula.jpg" />
</div>
</section>
section {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 400px;
background: green;
}
#slider {
height: 100%; /* this is wrong; how to set height to stretch element? */
}
img {
width: 100%;
height: 100%;
}
Header is set to 40px just for the sake of the example. It could be any other value, but the CSS definition of other elements shouldn't be aware of that, because it's dynamically loaded 3rd party component with inline CSS.
Also, slider div is a complex slider (Swiper) that renders code with bunch of nested divs but I need to use exactly that one.
But this DOM structure should be rough sketch of my case.
Example is here: https://jsfiddle.net/snaokLxd/3/

Set the parent to flex with #slider set to flex-grow: 1 (or flex: 1 0 0 for short)
section {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 400px;
background: green;
display: flex;
flex-direction: column;
}
#slider {
flex: 1 0 0;
}
img {
width: 100%;
height: 100%;
vertical-align: top;
}
<section>
<header style="height:40px; background: yellow;">header</header>
<div id="slider">
<img src="http://amanita-design.net/img/home-news/botanicula.jpg" />
</div>
</section>

Flex example below:
* {
margin: 0;
}
section {
height: 100vh;
width: 400px;
background: green;
display: flex;
flex-direction: column;
}
#slider {
flex-grow: 2;
}
img {
display: block;
width: 100%;
height: 100%;
}
<section>
<header style="height:40px; background: yellow;">header</header>
<div id="slider">
<img src="http://amanita-design.net/img/home-news/botanicula.jpg" />
</div>
</section>
Also on JSFiddle.

Related

how can we use flexbox and responsive units like % for several images?

I want to put three images next together by using flexbox and responsive units in this case percentage(%). I have 2 issues: (1) justify-content does not work because I use % for flex items. (2) The tall of my images are getting bigger than the wide, in other word width and height of my images are not relative together because I used auto keyword for the height of images so I can have width and height relative. I would be very grateful if you could help me. I will include my code below.
.services h2 {
margin: 3.125rem 0 2rem;
}
#services-container {
display: flex;
width: 100%;
}
.service-item {
width: 90%;
height: 65%;
overflow: hidden;
}
.service-item img {
width: 100%;
height: auto;
display: block;
}
<section class="services">
<h2>our services</h2>
<div id="services-container">
<div class="service-item hotel">
<img src="./resources/pictures/hotel.jpg" alt="hotel">
<h4>The best hotels</h4>
</div>
<div class="service-item air-plane">
<img src="./resources/pictures/airplane.jpg" alt="airplane">
<h4>The best airline services</h4>
</div>
<div class="service-item food">
<img src="./resources/pictures/food.jpg" alt="food">
<h4>The highest quality foods</h4>
</div>
</div>
</section>
First, you can set the height to 100% and width to auto and use justify-content on parent like so :
.services h2 {
margin: 3.125rem 0 2rem;
}
#services-container {
display: flex;
width: 100%;
justify-content: space-between;
}
.service-item {
width: 30%;
height: 100%;
overflow: hidden;
}
.service-item img {
width: auto;
height: 100%;
display: block;
}
Hope it helps

Can't define relative vertical position of div within nested structure

I'm using Swipe.js to create a page with several screens. Swipe requires a structure of 3 nested divs, with some style defined. I want to position an element 70% towards the bottom of one of the screens, but I'm finding that its Y position remains at the top when defined as a percentage. My guess is that the height of the containing div is somehow still 0, though I have set all min-height properties to 100%.
I'm testing on Chrome in desktop, for now. My stylesheet:
/* required by swipe.js */
.swipe {
overflow: hidden;
position: relative;
min-height: 100%; /* added this everywhere I could just in case */
}
.swipe-wrap {
overflow: hidden;
position: relative;
min-height: 100%;
}
.swipe-wrap > div {
float: left;
width: 100%;
min-height: 100%;
position: relative;
}
.page {
min-height: 100%;
}
html,body {
height: 100%;
margin: 0px;
padding: 0px;
}
/* element I want to position */
.myElement {
position: relative;
width: 200px;
top: 70%;
left: 50%;
transform: translate(-50%, -50%);
}
Body:
<div id="slider" class="swipe">
<div class="swipe-wrap">
<div class="page">
<div class="myElement">
<h1>I should be more than halfway down.</h1>
</div>
</div>
</div>
</div>
The result is that the inner div is centred horizontally, but vertically it's at the top (in fact, cut off because of the transform offset).
I have tried using flex and align-items: center. That does work. I'm not sure if I can use flex to define arbitrary relative positions, though.
Please check below example
.swipe {
overflow: hidden;
position: relative;
height: 100%;
}
.swipe-wrap {
overflow: hidden;
position: relative;
height: 100%;
}
.swipe-wrap > .page {
display: table;
width: 100%;
height: 100%;
table-layout: fixed;
text-align: center;
}
.myElement{
display: table-cell;
vertical-align: middle;
}
.page {
min-height: 100%;
}
html,body {
height: 100%;
margin: 0px;
padding: 0px;
}
<div id="slider" class="swipe">
<div class="swipe-wrap">
<div class="page">
<div class="myElement">
<h1>I should be more than halfway down.</h1>
</div>
</div>
</div>
</div>

Full width and height empty state?

Trying to make an empty state placed below an app-header. I want it to take the full width and full height (minus the height of the app-header) of the entire screen.
So far, I have this, but it doesn't seem to work:
<app-header-layout>
<app-header>
.....
</app-header>
<div id="empty-state" style="width: 100%; height: 100%; background: #F00; white-space: nowrap;">
.....
</div>
</app-header-layout>
Still no go. The empty state has the full width that I want, but not the height that I want.
Please help me out.
There's a couple way to do this. You can use a flex-box (explained here):
body,
html {
height: 100%;
}
body {
margin: 0px;
display: flex;
flex-direction: column;
}
app-header {
min-height: 50px;
}
#empty-state {
overflow: auto;
background: #F00;
flex-grow: 1;
overflow: auto;
}
<body>
<app-header>...</app-header>
<div id="empty-state">
.....
</div>
</body>
You can also simply absolutely position your elements. Simply account for the height of app-header and offset #empty-state by this height. Like this:
body,
html {
height: 100%;
}
body {
margin: 0;
}
app-header {
width: 100%;
height: 50px;
}
#empty-state {
position: absolute;
top: 50px;
bottom: 0;
background: #F00;
width: 100%;
}
<app-header-layout>
<app-header>
.....
</app-header>
<div id="empty-state">
.....
</div>
</app-header-layout>

position sticky automatic "top" position

Is there a way for stickies to take into account other stickes on the page?
For example:
body {
display: flex;
min-height: 2000px;
flex-direction: column;
}
#header {
height: 40px;
flex: 0 0 auto;
position: sticky;
top: 0;
background: yellow;
}
#footer {
flex: 0 0 auto;
height: 20px;
}
#main {
display: flex;
flex: auto;
background: blue;
}
#side {
width: 200px;
background: red;
}
#side > div {
position: sticky;
top: 0px;
}
<div id="header">header</div>
<div id="main">
<div id="side">
<div>side</div>
</div>
<div id="content">
content
</div>
</div>
<div id="footer">footer</div>
Notice that if I scroll down the header will overlap the sidebar because they have the same top position.
To fix I have to make the top position of the sidebar take the value of the header height
body {
display: flex;
min-height: 2000px;
flex-direction: column;
}
#header {
height: 40px;
flex: 0 0 auto;
position: sticky;
top: 0;
background: yellow;
}
#footer {
flex: 0 0 auto;
height: 20px;
}
#main {
display: flex;
flex: auto;
background: blue;
}
#side {
width: 200px;
background: red;
}
#side > div {
position: sticky;
top: 40px;
}
<div id="header">header</div>
<div id="main">
<div id="side">
<div>side</div>
</div>
<div id="content">
content
</div>
</div>
<div id="footer">footer</div>
But what if the header has variable height? Can I tell the browser somehow to position the stickies so they dont overlap others?
I think you would need to use javascript for this. First to get the height of the header and then set the top position of your side div using that value. I am not aware of any pure css way of doing it I am afraid.
If you are using jQuery it is simply using the .height() method if not you can use this:
var clientHeight = document.getElementById('myDiv').clientHeight;
var offsetHeight = document.getElementById('myDiv').offsetHeight;
The offset method gets the height with any padding and borders.

Child with max-height: 100% overflows parent

I'm trying to understand what appears to be unexpected behaviour to me:
I have an element with a max-height of 100% inside a container that also uses a max-height but, unexpectedly, the child overflows the parent:
.container {
background: blue;
padding: 10px;
max-height: 200px;
max-width: 200px;
}
img {
display: block;
max-height: 100%;
max-width: 100%;
}
<div class="container">
<img src="http://placekitten.com/400/500" />
</div>
This is fixed, however, if the parent is given an explicit height:
.container {
background: blue;
padding: 10px;
max-height: 200px;
max-width: 200px;
height: 200px;
}
img {
display: block;
max-height: 100%;
max-width: 100%;
}
<div class="container">
<img src="http://placekitten.com/400/500" />
</div>
Does anyone know why the child would not honour the max-height of its parent in the first example? Why is an explicit height required?
When you specify a percentage for max-height on a child, it is a percentage of the parent's actual height, not the parent's max-height, oddly enough. The same applies to max-width.
So, when you don't specify an explicit height on the parent, then there's no base height for the child's max-height to be calculated from, so max-height computes to none, allowing the child to be as tall as possible. The only other constraint acting on the child now is the max-width of its parent, and since the image itself is taller than it is wide, it overflows the container's height downwards, in order to maintain its aspect ratio while still being as large as possible overall.
When you do specify an explicit height for the parent, then the child knows it has to be at most 100% of that explicit height. That allows it to be constrained to the parent's height (while still maintaining its aspect ratio).
.container {
background: blue;
padding: 10px;
max-height: 200px;
max-width: 200px;
float: left;
margin-right: 20px;
}
.img1 {
display: block;
max-height: 100%;
max-width: 100%;
}
.img2 {
display: block;
max-height: inherit;
max-width: inherit;
}
<!-- example 1 -->
<div class="container">
<img class='img1' src="http://via.placeholder.com/350x450" />
</div>
<!-- example 2 -->
<div class="container">
<img class='img2' src="http://via.placeholder.com/350x450" />
</div>
I played around a little. On a larger image in firefox, I got a good result with using the inherit property value. Will this help you?
.container {
background: blue;
padding: 10px;
max-height: 100px;
max-width: 100px;
text-align:center;
}
img {
max-height: inherit;
max-width: inherit;
}
Instead of going with max-height: 100%/100%, an alternative approach of filling up all the space would be using position: absolute with top/bottom/left/right set to 0.
In other words, the HTML would look like the following:
<div class="flex-content">
<div class="scrollable-content-wrapper">
<div class="scrollable-content">
1, 2, 3
</div>
</div>
</div>
.flex-content {
flex-grow: 1;
position: relative;
width: 100%;
height: 100%;
}
.scrollable-content-wrapper {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: auto;
}
.scrollable-content {
/* Add styling here */
}
Try it below:
.flex-content {
flex-grow: 1;
position: relative;
width: 100%;
height: 100%;
}
.scrollable-content-wrapper {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: auto;
}
html {
height: 50%;
width: 50%;
}
body {
height: 100%;
width: 100%;
}
.parent {
height: 100%;
outline: 1px solid red;
}
<html>
<body>
<div class="parent">
<div class="flex-content">
<div class="scrollable-content-wrapper">
<div class="scrollable-content" id="scrollable">
1, 2, 3
</div>
</div>
</div>
</div>
<button onClick="scrollable.innerText += '\nSome more text'" style="margin-top: 1rem;">Add Line</button>
<p>
The red outline represents the parent. Click above to add a line until overflow occurs to see that the size of the parent is not increased.
</p>
</body>
</html>
I found a solution here:
http://www.sitepoint.com/maintain-image-aspect-ratios-responsive-web-design/
The trick is possible because it exists a relation between WIDTH and PADDING-BOTTOM of an element. So:
parent:
container {
height: 0;
padding-bottom: 66%; /* for a 4:3 container size */
}
child (remove all css related to width, i.e. width:100%):
img {
max-height: 100%;
max-width: 100%;
position: absolute;
display:block;
margin:0 auto; /* center */
left:0; /* center */
right:0; /* center */
}
You can use the property object-fit
.cover {
object-fit: cover;
width: 150px;
height: 100px;
}
Like suggested here
A full explanation of this property by Chris Mills in Dev.Opera
And an even better one in CSS-Tricks
It's supported in
Chrome 31+
Safari 7.1+
Firefox 36+
Opera 26+
Android 4.4.4+
iOS 8+
I just checked that vivaldi and chromium support it as well (no surprise here)
It's currently not supported on IE, but... who cares ? Also, iOS supports object-fit, but not object-position, but it will soon.
Here is a solution for a recently opened question marked as a duplicate of this question. The <img> tag was exceeding the max-height of the parent <div>.
Broken: Fiddle
Working: Fiddle
In this case, adding display:flex to the 2 parent <div> tags was the answer
Maybe someone else can explain the reasons behind your problem but you can solve it by specifying the height of the container and then setting the height of the image to be 100%. It is important that the width of the image appears before the height.
<html>
<head>
<style>
.container {
background: blue;
padding: 10px;
height: 100%;
max-height: 200px;
max-width: 300px;
}
.container img {
width: 100%;
height: 100%
}
</style>
</head>
<body>
<div class="container">
<img src="http://placekitten.com/400/500" />
</div>
</body>
</html>
The closest I can get to this is this example:
http://jsfiddle.net/YRFJQ/1/
or
.container {
background: blue;
border: 10px solid blue;
max-height: 200px;
max-width: 200px;
overflow:hidden;
box-sizing:border-box;
}
img {
display: block;
max-height: 100%;
max-width: 100%;
}
The main problem is that the height takes the percentage of the containers height, so it is looking for an explicitly set height in the parent container, not it's max-height.
The only way round this to some extent I can see is the fiddle above where you can hide the overflow, but then the padding still acts as visible space for the image to flow into, and so replacing with a solid border works instead (and then adding border-box to make it 200px if that's the width you need)
Not sure if this would fit with what you need it for, but the best I can seem to get to.
A good solution is to not use height on the parent and use it just on the child with View Port :
Fiddle Example: https://jsfiddle.net/voan3v13/1/
body, html {
width: 100%;
height: 100%;
}
.parent {
width: 400px;
background: green;
}
.child {
max-height: 40vh;
background: blue;
overflow-y: scroll;
}
Containers will already generally wrap their content nicely. It often doesn't work as well the other way around: children don't fill their ancestors nicely. So, set your width/height values on the inner-most element rather than the outer-most element, and let the outer elements wrap their contents.
.container {
background: blue;
padding: 10px;
}
img {
display: block;
max-height: 200px;
max-width: 200px;
}
http://jsfiddle.net/mpalpha/71Lhcb5q/
.container {
display: flex;
background: blue;
padding: 10px;
max-height: 200px;
max-width: 200px;
}
img {
object-fit: contain;
max-height: 100%;
max-width: 100%;
}
<div class="container">
<img src="http://placekitten.com/400/500" />
</div>

Resources