CSS flexbox overflow 100% width elements - html

On mouse over, I want to transform the position of two flex element that are sitting next to each other as per the image below
The markup is as follows
<div class="container">
<div class="element">Normal</div>
<div class="element">Hover</div>
</div>
I want both elements to be 100% width of the parent and the second element to overflow so I can transform X on mouse over. The problem I'm having is both elements get squeezed in to the container.
I know I can wrap the two elements in another div and give it 200% width of container. But want to know if this can be done with flexbox

.container {
display: flex;
width: 200px;
overflow: hidden; /* hides the flex item parked outside the container */
cursor: pointer;
border: 1px solid black;
}
.element {
height: 100px;
flex: 0 0 100%; /* can't grow, can't shrink, fixed an 100% width */
transition: .5s;
min-width: 0; /* override min-width: auto default;
https://stackoverflow.com/q/36247140/3597276 */
}
.container:hover > .element:first-child {
flex-basis: 0;
}
.element:first-child { background-color: lightgreen; }
.element:last-child { background-color: orange; }
<div class="container">
<div class="element">Normal</div>
<div class="element">Hover</div>
</div>
You wrote:
The problem I'm having is both elements get squeezed in to the container.
That was probably because flex items are set, by default, to flex-shrink: 1, meaning they are permitted to shrink in order to fit inside the container. In my answer flex-shrink is set to 0.

Related

Why flex item with flex-basis: auto doesn't get all available space if its content's width is 100%?

.parent {
display: flex;
border: 1px solid black;
}
.first>input {
display: block;
width: 100%;
padding: 0;
}
<div class="parent">
<div class="first"><input type="text"></div>
<div class="second"><button>Button</button></div>
</div>
In this sample I'm doing something with the input's styles that will shrink its width as the .parent's width becomes smaller itself. However, it puzzles me why, as long as the .parent's width is more than enough, the .first>input brotherhood don't take up all the available space? There are no max-width set on them, so why should they freeze up in a flex container? What's the rules here?
flex-basis: auto looks up the main size of the element and defines the size. For example, on a horizontal flex container, auto will look for width and height if the container axis is vertical. If no size is specified, auto will fall back to content.
~ Flex Basis Property in Flexbox
So in your case, no size was specified on the flex container. Set the flex-basis on the parent of the element you are trying to grow. In your case, it would be .first.
.parent {
display: flex;
align-items: center;
border: 1px solid black;
}
.first>input {
display: block;
width: 100%;
padding: 0;
}
.first {
flex-basis: 100%;
}
<div class="parent">
<div class="first"><input type="text"></div>
<div class="second"><button>Button</button></div>
</div>

How to calculate the actual width of child elements of a flexbox container?

I have such html and css code. I have two questions:
When the window width is 600px, in theory, the left box should be of 150px width, because it's the 25% of the parent(600px), but the width is 120px.
The right box can not reach 100vw. It just takes the widthOfParent - widthOfLeft. I suppose it should somehow overflow and reach 100vw.
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
* {
margin: 0;
padding: 0;
}
.container {
height: 300px;
/* your code here */
display: flex;
justify-content: start;
}
.left {
background-color: #f44336;
height: 100%;
width: 25%;
min-width: 100px;
}
.right {
background-color: #2973af;
height: 100%;
width: 100vw;
}
codesanbox: https://codesandbox.io/s/admiring-darkness-cu99x?file=/src/styles.css:0-293
You are facing the shrink effect. In all the cases the total width 100vw + 25% is bigger than 100% so both items will shrink equally.
Since your container is also full width, the overflow will always be equal to 25% or 25vw. Both element have the default shrink value 1 so we have a sum equal to 125vw.
The first element width will be equal to: 25vw - (25vw * 25vw/125vw) = 20vw
and the width of the second item will be: 100vw - (25vw * 100vw/125vw) = 80vw
You can logically see that the total is 100vw (20vw + 80vw) and when the screen width is equal to 600px, 20vw is equal 120px.
To avoid this, disable the shrink effect on the first item by setting flex-shrink:0
* {
margin: 0;
padding: 0;
}
.container {
height: 300px; /* your code here */
display: flex;
}
.left {
background-color: #f44336;
width: 25%;
min-width: 100px;
flex-shrink:0;
}
.right {
background-color: #2973af;
width: 100vw;
}
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
Related: Why is a flex item limited to parent size?
use "flex" property instead of width for the flex items
.container {
height: 300px;
width: 600px; /* added fixed width for testing */
display: flex;
justify-content: start;
}
.left {
background-color: #f44336;
min-width: 100px;
flex: 1; /* 1/4 ratio within the parent element, as the other flex item in the parent is "flex: 3;" */
}
.right {
background-color: #2973af;
flex: 3; /* 3/4 ratio within the parent element, as the other flex item in the parent is "flex: 1;" */
}
The right element cannot take 100% of the width, as it is together with the left div inside the flex parent and we assigned the width parameter as "flex: value" for both
CSS flex Property
https://www.w3schools.com/cssref/css3_pr_flex.asp

Get a full height div from where it starts according to the window

This question is more project specific. I have two <div> in my template which looks like this.
I want that last div to acquire full height respective to window from its vertical position.
For example,
If I add another div, it should look like this.
Right now in this example, I am setting height explicitly just for demonstration purpose.
How can I make the last div calculate height according to its vertical position and all content should be visible inside the window.
You can use flexbox for this. The flow is column so the div's are vertically aligned. The last div get a flex:1 to expand.
flex:1 or flex:1 1 0% is a short writing for:
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
For your last div the flex-grow and the flex-basis is important.
See flex property
body {
height: 100vh;
}
.container {
display: flex;
flex-flow: column nowrap;
height: 100%;
}
.container>div {
background: #ccc;
border-top: 3px solid white;
}
.container>div:last-child {
flex: 1;
}
<div class="container">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
.container {
display: block;
position: fixed;
top: 0; /* where you want to start; */
bottom: 0; /* (the end) */
left: 0;
/* you can use width 100% or code right:0; */
}
<div class="container">
somethin
</div>

flexbox parent smaller than child elements on browser resize [duplicate]

This question already has answers here:
Why don't flex items shrink past content size?
(5 answers)
Closed 5 years ago.
Why is the parent smaller than the children on narrow screens?
See snippet... then resize your browser (width-wise) to be smaller than the pink box. The scroll bars should appear. Scroll back to the right on the page and note the green background is smaller than the pink area and there is a white spot on the right.
So few questions:
Why does it happen?
How do I prevent the parent div's green background from getting smaller than the pink box/div when the browser is resized without setting an explicit width on the parent (or anywhere else) or using overflow:hidden?
Is there a flexbox solution to this problem?
Thanks,
Thomas
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
Flex items, by default, cannot be smaller than the size of their content. Therefore, while the parent can shrink, the flex items cannot shrink past the length of the text. This causes the overflow and, as a result, the column of white space below.
The initial setting on flex items is min-width: auto. In order for each item to stay within the container, switch to min-width: 0.
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
min-width: 0; /* NEW */
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
Now the pink boxes are not overflowing the container anymore.
However, the text is now overflowing the pink boxes.
The question doesn't specify behavior for the text, but here's one possible solution:
.parent {
height: 400px;
background-color: green;
display: flex;
}
.item {
height: 100px;
background-color: pink;
padding: 10px;
min-width: 0; /* NEW */
text-overflow: ellipsis; /* NEW */
white-space: nowrap; /* NEW */
overflow: hidden; /* NEW */
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
It happens, because your .parent is a normal block element (flex is also a block-level container) and that is initialized with width:auto;, which is in your case the width of the viewport. So scrolling to the right will show white space because your div's width is smaller than the whole scrollable area.
You do that with setting the .parent to an inline element, which will respond to its childrens width.
Yes, just use display: inline-flex; on the .parent.
.parent {
height: 400px;
width: 100%;
background-color: green;
display: inline-flex;
}
.item {
flex: 1;
height: 100px;
background-color: pink;
padding: 10px;
}
<div class="parent">
<div class="item">looooooooooooooooooooooooooooong</div>
<div class="item">looooooooooooooooooooooooooooong</div>
</div>
See display on MDN.

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