I have an image in a flex container (let's call it child).
The container itself has flex: 1 because it also belongs to a flex parent with flex-direction: column and child behaves as expected (takes 100% height of parent) before introducing the image situation.
On adding an image to child, the image extends child to accommodate its height, effectively child pushing parent.
Using object-fit: cover on the image could not help in this situation!
Looks like this is normal behaviour in Chrome, Firefox, and Safari.
Here's the weird part tho: adding a height property to child fixes the issue on Chrome and Safari but not Firefox. This height's value doesn't matter much as long as it's not greater than the computed height of child - child is contained to 100% height of parent together with the image - even if the height value of child is set to 1px. Surprisingly height: 100% on child doesn't produce the same behaviour.
Any ideas around this will be much appreciated. My goal is to make the image always take up 100% of child - regardless of the width of the viewport.
Here's some code for illustration
#parent {
height: 100vh;
display: flex;
flex-direction: column;
}
#child {
flex: 1;
height: 1px;
/* the weird hack for chrome and safari - ff ignores it */
}
#child img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
</div>
I also added some images for more context. These are crops of the entire page. The first image is from a smaller viewport (width shared with devtools) and the second from the entire page (devtools hidden). The second image shows that the image has extended child to eventually push the container with a green background beyond the fold - causing a scrollbar to appear.
child is the image wrapper, and parent is the component with a white background and some padding. You realize parent has a sibling (green background) and their parent (container with a green border and border radius) has a display: grid and grid-template-rows: 1fr auto;.
It should also be noted that the most top-level element has a height of 100vh and all its children are rendered within that. Essentially there should not be a scrollbar.
I hope you get the idea.
Is this what you are looking to do?
You can also see it here: https://codepen.io/teanbiscuits/pen/GRJmPgo
#parent {
height: 100vh;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
border:2px solid green;
border-radius:20px;
overflow:hidden;
}
#child {
position:relative;
}
#child img {
position:absolute;
width: 100%;
height: 100%;
object-fit: contain;
}
#text {
background-color:green;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
<div id="text">
<h2>some title here</h2>
<p>Some description here</p>
</div>
</div>
Please try this. Just select preferred object-fit.
html, body{
height: 100%;
margin: 0;
padding: 0;
}
#parent {
height: calc(100vh - 20px);
display: flex;
padding: 10px;
flex-direction: column;
}
#child {
flex: 1;
text-align: center;
overflow: hidden;
}
#child img {
width: 100%;
height: 100%;
object-fit: contain;
}
<div id="parent">
<div id="child">
<img src="https://i.picsum.photos/id/1005/400/1000.jpg" />
</div>
<div id="text">
<h2>Some title here</h2>
<p>Some description here</p>
</div>
</div>
I have a div with an image inside.
The div has a fixed height, lower than the image intrinsic height.
I want the div's width to be always equal to the "displayed" width of the image (which is a function of the unknown div's height and image's height/width ratio).
Here is a codepen example of what I want to do (having "shrink" and "other" divs the same width than their child image)
<div class="container">
<div class="shrink">
<img src="https://dummyimage.com/600x400/0f0/fff" />
</div>
<div class="other">
<img src="https://dummyimage.com/600x400/00f/fff" />
</div>
</div>
div { margin: 5px; }
img { max-height: 100%; }
.container {
background-color: red;
display: flex;
flex-flow: row nowrap;
width: 800px;
height: 100px;
overflow: hidden;
}
.shrink {
flex: 0 1 auto;
background-color: #88FF88;
}
.other {
flex: 0 1 auto;
background-color: #8888FF;
}
Is it possible with only HTML/CSS ie without Javascript ?
Thanks in advance.
Remove width as an attribute of your container and the div will conform to the width of its contents.
Does this work for you?
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>
I have an image in a container div below which is a div containing text that can scroll. The image can vary in size so the height of the image will vary depending on the image being displayed i.e. it isn't always 400 x 200 as in my example.
My problem is that when the text scrolls there is a space between the image and the point where the text should scroll behind the image. This seems to be because the image container div is not the same size as the image.
This JSFiddle shows the problem https://jsfiddle.net/t0ag2z5k/50/
Can anyone tell me both why this is happening and how to fix it please?
CSS code below...
#plotdetails {
display: flex;
flex-direction: column;
float: left;
width: 400px;
z-index: 5;
position: fixed;
height: 100%;
}
#plotdetails #plot-img-container {
display: inline-block;
}
#plotdetails #plot-img-container img{
width: 400px;
display: inline-block;
}
#details-text {
padding: 0 20px 0 20px;
overflow-y: scroll;
flex: 1;
}
HTML here
<div id="plotdetails">
<div id="plot-img-container">
<img src="http://placehold.it/400x200">
</div>
<div id="details-text">
<h1>Lot's of words here....</h1>
</div>
</div>
Why do you want your image to be display as an inline-block when it's not utilized as such? Try (https://jsfiddle.net/t0ag2z5k/52/):
#plotdetails #plot-img-container img{
width: 400px;
display: block;
}
How can I achieve the following structure without using tables or JavaScript? The white borders represent edges of divs and aren't relevant to the question.
The size of the area in the middle is going to vary, but it will have exact pixel values and the whole structure should scale according to those values. To simplify it, I'd need a way to set "100% - n px" width to the top-middle and bottom-middle divs.
I'd appreciate a clean cross-browser solution, but in case it's not possible, CSS hacks will do.
Here's a bonus. Another structure I've been struggling with and end up using tables or JavaScript. It's slightly different, but introduces new problems. I've been mainly using it in jQuery-based windowing system, but I'd like to keep the layout out of the script and only control the size of one element (the middle one).
New way I've just stumbled upon: css calc():
.calculated-width {
width: -webkit-calc(100% - 100px);
width: -moz-calc(100% - 100px);
width: calc(100% - 100px);
}
Source: css width 100% minus 100px
You can use nested elements and padding to get a left and right edge on the toolbar. The default width of a div element is auto, which means that it uses the available width. You can then add padding to the element and it still keeps within the available width.
Here is an example that you can use for putting images as left and right rounded corners, and a center image that repeats between them.
The HTML:
<div class="Header">
<div>
<div>This is the dynamic center area</div>
</div>
</div>
The CSS:
.Header {
background: url(left.gif) no-repeat;
padding-left: 30px;
}
.Header div {
background: url(right.gif) top right no-repeat;
padding-right: 30px;
}
.Header div div {
background: url(center.gif) repeat-x;
padding: 0;
height: 30px;
}
While Guffa's answer works in many situations, in some cases you may not want the left and/or right pieces of padding to be the parent of the center div. In these cases, you can use a block formatting context on the center and float the padding divs left and right. Here's the code
The HTML:
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div class="center"></div>
</div>
The CSS:
.container {
width: 100px;
height: 20px;
}
.left, .right {
width: 20px;
height: 100%;
float: left;
background: black;
}
.right {
float: right;
}
.center {
overflow: auto;
height: 100%;
background: blue;
}
I feel that this element hierarchy is more natural when compared to nested nested divs, and better represents what's on the page. Because of this, borders, padding, and margin can be applied normally to all elements (ie: this 'naturality' goes beyond style and has ramifications).
Note that this only works on divs and other elements that share its 'fill 100% of the width by default' property. Inputs, tables, and possibly others will require you to wrap them in a container div and add a little more css to restore this quality. If you're unlucky enough to be in that situation, contact me and I'll dig up the css.
jsfiddle here: jsfiddle.net/RgdeQ
Enjoy!
You can make use of Flexbox layout. You need to set flex: 1 on the element that needs to have dynamic width or height for flex-direction: row and column respectively.
Dynamic width:
HTML
<div class="container">
<div class="fixed-width">
1
</div>
<div class="flexible-width">
2
</div>
<div class="fixed-width">
3
</div>
</div>
CSS
.container {
display: flex;
}
.fixed-width {
width: 200px; /* Fixed width or flex-basis: 200px */
}
.flexible-width {
flex: 1; /* Stretch to occupy remaining width i.e. flex-grow: 1 and flex-shrink: 1*/
}
Output:
.container {
display: flex;
width: 100%;
color: #fff;
font-family: Roboto;
}
.fixed-width {
background: #9BCB3C;
width: 200px; /* Fixed width */
text-align: center;
}
.flexible-width {
background: #88BEF5;
flex: 1; /* Stretch to occupy remaining width */
text-align: center;
}
<div class="container">
<div class="fixed-width">
1
</div>
<div class="flexible-width">
2
</div>
<div class="fixed-width">
3
</div>
</div>
Dynamic height:
HTML
<div class="container">
<div class="fixed-height">
1
</div>
<div class="flexible-height">
2
</div>
<div class="fixed-height">
3
</div>
</div>
CSS
.container {
display: flex;
}
.fixed-height {
height: 200px; /* Fixed height or flex-basis: 200px */
}
.flexible-height {
flex: 1; /* Stretch to occupy remaining height i.e. flex-grow: 1 and flex-shrink: 1*/
}
Output:
.container {
display: flex;
flex-direction: column;
height: 100vh;
color: #fff;
font-family: Roboto;
}
.fixed-height {
background: #9BCB3C;
height: 50px; /* Fixed height or flex-basis: 100px */
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
.flexible-height {
background: #88BEF5;
flex: 1; /* Stretch to occupy remaining width */
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
<div class="container">
<div class="fixed-height">
1
</div>
<div class="flexible-height">
2
</div>
<div class="fixed-height">
3
</div>
</div>
The usual way to do it is as outlined by Guffa, nested elements. It's a bit sad having to add extra markup to get the hooks you need for this, but in practice a wrapper div here or there isn't going to hurt anyone.
If you must do it without extra elements (eg. when you don't have control of the page markup), you can use box-sizing, which has pretty decent but not complete or simple browser support. Likely more fun than having to rely on scripting though.
Maybe I'm being dumb, but isn't table the obvious solution here?
<div class="parent">
<div class="fixed">
<div class="stretchToFit">
</div>
.parent{ display: table; width 100%; }
.fixed { display: table-cell; width: 150px; }
.stretchToFit{ display: table-cell; vertical-align: top}
Another way that I've figured out in chrome is even simpler, but man is it a hack!
.fixed{
float: left
}
.stretchToFit{
display: table-cell;
width: 1%;
}
This alone should fill the rest of the line horizontally, as table-cells do. However, you get some strange issues with it going over 100% of its parent, setting the width to a percent value fixes it though.
We can achieve this using flex-box very easily.
If we have three elements like Header, MiddleContainer and Footer. And we want to give some fixed height to Header and Footer. then we can write like this:
For React/RN(defaults are 'display' as flex and 'flexDirection' as column), in web css we'll have to specify the body container or container containing these as display: 'flex', flex-direction: 'column' like below:
container-containing-these-elements: {
display: flex,
flex-direction: column
}
header: {
height: 40,
},
middle-container: {
flex: 1, // this will take the rest of the space available.
},
footer: {
height: 100,
}
what if your wrapping div was 100% and you used padding for a pixel amount, then if the padding # needs to be dynamic, you can easily use jQuery to modify your padding amount when your events fire.
I had a similar issue where I wanted a banner across the top of the screen that had one image on the left and a repeating image on the right to the edge of the screen. I ended up resolving it like so:
CSS:
.banner_left {
position: absolute;
top: 0px;
left: 0px;
width: 131px;
height: 150px;
background-image: url("left_image.jpg");
background-repeat: no-repeat;
}
.banner_right {
position: absolute;
top: 0px;
left: 131px;
right: 0px;
height: 150px;
background-image: url("right_repeating_image.jpg");
background-repeat: repeat-x;
background-position: top left;
}
The key was the right tag. I'm basically specifying that I want it to repeat from 131px in from the left to 0px from the right.
In some contexts, you can leverage margin settings to effectively specify "100% width minus N pixels". See the accepted answer to this question.