Flexbox: Why isn't whole div shown when element below becomes hidden? - css

I need a flexible column where a couple of widgets are placed below each other and they are supposed to take up space dynamically. A widget has a title, and a scrollable content.
The last widget is supposed to be collapsible (by clicking on the widget title).
The problem is: When I collapse the widget, part of the title becomes hidden.
See here (click "Batch runs" to see the problem):
http://jsfiddle.net/stoefln/Ls0aqnvf/8/
$('.batchRunsTitle').on('click', function() {
$('.batchRuns').toggle()
})
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="column" style="height: calc(100% - 80px); background: #AAA; display: flex; flex-direction: column; ">
<div class="batchView" style="flex-grow: 1; display: flex; flex-direction: column; border: 1px solid #F00; overflow: hidden;">
<div class="header" style="">widget title 1</div>
<div class="tests" style="flex-basis: 70%; border: 1px solid #F0F; overflow: auto;">test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/></div>
<div>
widget title 2
</div>
<div class="ehs" style="flex-basis: 30%; border: 1px solid #00F; overflow: auto;">test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/></div>
</div>
<div class="batchRunsContainer" style="flex-grow: 1; display: flex; flex-direction: column; overflow: auto">
<div class="batchRunsTitle" style="cursor: pointer; background-color: #6c2; border: 10px solid #55F; ">
widget title 3
</div>
<!-- why is the "Batch runs" title only half visible when the next block gets hidden??? -->
<div class="batchRuns" style="overflow: auto; display: block;">
test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>
</div>
</div>
</div>
<div class="log" style="height: 80px; background-color: #EEAEEE66; position: absolute; bottom: 0; right: 0; left: 0;">
Log
</div>
Also for reference here the 2 states as screenshots:
Open:
Collapsed:

Looks like it is being cut due to the overflow: auto on the batchRunsContainer. One way to fix this is to put the batchRunsTitle div outside the batchRunsContainer. Here is a working demo (check in full page):
const title = document.querySelector('.batchRunsTitle')
const content = document.querySelector('.batchRuns')
title.addEventListener('click', () => {
if (content.style.display !== 'none') {
content.style.display = 'none'
} else {
content.style.display = ''
}
})
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
width: 100%;
}
<div class="column" style="height: calc(100% - 80px); background: #AAA; display: flex; flex-direction: column; ">
<div class="batchView" style="flex-grow: 1; display: flex; flex-direction: column; border: 1px solid #F00; overflow: hidden;">
<div class="header" style="">widget title 1</div>
<div class="tests" style="flex-basis: 70%; border: 1px solid #F0F; overflow: auto;">test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/>test<br/></div>
<div>
widget title 2
</div>
<div class="ehs" style="flex-basis: 30%; border: 1px solid #00F; overflow: auto;">test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/>test2<br/></div>
</div>
<div class="batchRunsTitle" style="cursor: pointer; background-color: #6c2; border: 10px solid #55F; ">
widget title 3
</div>
<div class="batchRunsContainer" style="flex-grow: 1; display: flex; flex-direction: column; overflow: auto">
<div class="batchRuns" style="overflow: auto; display: block;">
test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>test3<br/>
</div>
</div>
</div>
<div class="log" style="height: 80px; background-color: #EEAEEE66; position: absolute; bottom: 0; right: 0; left: 0;">
Log
</div>

Related

How to use "flex" and "gap" for a grid with different-sized items? (gap is not working)

My code:
<head>
<style>
*, *:before, *:after {
box-sizing: border-box;
}
html{
background-color: #aaa;
width: 100%;
height: 100%;
}
body {
background-color: #aaa;
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
.item {
background-color: #fff;
}
.d-flex-column {
display: flex;
flex-direction: column;
}
.d-flex-row{
display: flex;
flex-direction: row;
}
.w-100{
width: 100%;
}
.h-100{
height: 100%;
}
</style>
</head>
<body>
<div class="d-flex-row w-100 h-100" style="gap: 0.5em;">
<div class="d-flex-column h-100" style="flex: 1; gap:0.5em;">
<div class="item" style="flex:2;">2</div>
<div class="item" style="flex:1;">1</div>
<div class="item" style="flex:1;">1</div>
</div>
<div class="d-flex-column h-100 " style="flex: 1; gap:0.5em;">
<div class="item" style="flex:3;">3</div>
<div class="item" style="flex:1;">1</div>
</div>
</div>
</body>
And the result:
If you look closely, the height of the item at the right bottom is sized differently to the left one.
Can I even use "flex: x;" and "gap"?
I know that I can achieve the same with "display: grid;" but I want to understand what I am doing wrong with this one.
This is because of the gap property: the left column have two 0.5em gaps whereas the right one only has one. if you use border to separate your cells instead you won't have this problem.
<head>
<style>
*, *:before, *:after {
box-sizing: border-box;
}
html{
background-color: #aaa;
width: 100%;
height: 100%;
}
body {
background-color: #aaa;
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
}
.item {
background-color: #fff;
}
.d-flex-column {
display: flex;
flex-direction: column;
}
.d-flex-row{
display: flex;
flex-direction: row;
}
.w-100{
width: 100%;
}
.h-100{
height: 100%;
}
.border {
border: 1px solid black;
}
</style>
</head>
<body>
<div class="d-flex-row w-100 h-100">
<div class="d-flex-column h-100" style="flex: 1">
<div class="item border" style="flex:2;">2</div>
<div class="item border" style="flex:1;">1</div>
<div class="item border" style="flex:1;">1</div>
</div>
<div class="d-flex-column h-100 " style="flex: 1">
<div class="item border" style="flex:3;">3</div>
<div class="item border" style="flex:1;">1</div>
</div>
</div>
</body>

How to fix div width using scss (with react)

I want to fix div width (image) and get scroll-bar. But the more add 'div', the smaller width of div in wrapper div area. It doesn't appear scroll-bar.
This is image-wrapper below.
<div className="image-list-wrapper">{imageList}</div>
{imageList} are added by clicking button with 'div'
let imageList;
if(images){
imageList = images
.map((image, i) => {
return(
<div key={i} className="image-list-item">
<img className="image-resize" src={`http://localhost:5000`+image}/>
</div>
)
})
}
and CSS (SCSS)
.image-list-wrapper {
margin: auto;
margin-top: 1.5rem;
width: 95%;
height: 12rem;
border: 1px solid #bcbaba;
overflow-x: auto;
display: flex;
flex-direction: row;
align-items: center;
}
.image-list-item {
float: left;
width: 9rem;
height: 9rem;
margin: 0.5rem;
margin-top: -0.5rem;
}
.image-resize {
width: 100px;
height: 160px;
}
I got result below (screenshot)(please ignore other buttons.)
screen shot
screen shot2
The more images, the smaller images.
I want scroll-bar and fixed width of div items in wrapper div.
Your code will generate something like this:
<div class="image-list-wrapper">
<div key="0" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="1" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="2" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="3" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="4" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="5" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
<div key="6" class="image-list-item">
<img class="image-resize" src="https://via.placeholder.com/200x160">
</div>
</div>
and you should remove float left because you either use floated elements or you use flexbox.
.image-list-wrapper {
margin: auto;
margin-top: 1.5rem;
width: 95%;
height: 12rem;
border: 1px solid #bcbaba;
overflow-x: auto;
display: flex;
flex-direction: row;
align-items: center;
}
.image-list-item {
width: 9rem;
height: 9rem;
margin: 0.5rem;
margin-top: -0.5rem;
}
.image-resize {
width: 100px;
height: 160px;
}
which results into this: Demo here

Empty cell in flexbox

There is a usual flexbox with the elements:
.flexbox {
display: flex;
border: 2px solid red;
padding: 2.5px;
}
.flexbox__item {
border: 2px solid blue;
height: 50px;
margin: 2.5px;
flex-grow: 1;
}
<div class="flexbox">
<div class="flexbox__item"></div>
<div class="flexbox__item offset-1"></div>
<div class="flexbox__item"></div>
</div>
How to insert an empty cell in this flexbox after .offset-* without using additional markup and after with before?
Those. It is necessary that it come out like this, but without specifying the width of the blocks. Only flex-grow
.flexbox {
display: flex;
border: 2px solid red;
padding: 2.5px;
}
.flexbox__item {
border: 2px solid blue;
height: 50px;
margin: 2.5px;
width: 25%;
}
.offset-1 {
margin-right: calc(25% + 5px);
}
<div class="flexbox">
<div class="flexbox__item"></div>
<div class="flexbox__item offset-1"></div>
<div class="flexbox__item"></div>
</div>
The number of cells is unknown. Offset-1 means that you need to make an empty one cell,offset-2 means two cell, and so on.
It is possible with additional wrappers - the wrappers will grow, and the cell elements inside the growing wrappers will occupy 1/2, 1/3, etc. of the width of the wrappers.
.flexbox {
display: flex;
border: 2px solid red;
padding: 3px;
}
.flexbox__item {
margin: 3px;
flex-grow: 1;
}
.flexbox__item-inner {
border: 2px solid blue;
height: 50px;
}
.offset-1 {
flex-grow: 2;
}
.offset-1 .flexbox__item-inner {
width: 50%;
}
.offset-2 {
flex-grow: 3;
}
.offset-2 .flexbox__item-inner {
width: 33.33%;
}
<div class="flexbox">
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item offset-1">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
</div>
<div class="flexbox">
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item offset-2">
<div class="flexbox__item-inner"></div>
</div>
<div class="flexbox__item">
<div class="flexbox__item-inner"></div>
</div>
</div>

Aligning flex items to the flex-end

Hei,
I'm creating grid layout and when trying to align items on flex-end or start, height/width becomes zero.
What I'm wondering is, is it possible to align items in flex container to the top/bottom/center WITHOUT specifying height/width? In my case I didn't specify any height and it doesn't work. If I specify it, it works. Line that doesn't work is commented in first example.
Here is code:
.flex-container {
display: flex;
background: silver;
width: 400px;
height: 200px;
//align-items: flex-start;
}
.flex-item {
background: blue;
flex: 1 0 10px;
margin: 5px;
}
.flex-container2 {
display: flex;
background: silver;
width: 400px;
height: 200px;
align-items: flex-end;
}
.flex-item2 {
background: blue;
height: 50px;
flex: 1 0 10px;
margin: 5px;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<br>
<div class="flex-container2">
<div class="flex-item2"></div>
<div class="flex-item2"></div>
<div class="flex-item2"></div>
<div class="flex-item2"></div>
</div>
As the default value for align-items is stretch, hence they fill their parents height, and you change it to flex-start/flex-end, the row flex items becomes 0 in height, and will need content (or a height/min-height) for you to actually see them.
.flex-container {
display: flex;
background: silver;
width: 400px;
height: 200px;
align-items: flex-start;
}
.flex-item {
background: blue;
min-height: 20px;
flex: 1 0 10px;
margin: 5px;
}
.flex-item:nth-child(even) {
align-self: flex-end;
}
.flex-container2 {
display: flex;
background: silver;
width: 400px;
height: 200px;
align-items: flex-end;
}
.flex-item2 {
background: blue;
height: 50px;
flex: 1 0 10px;
margin: 5px;
}
<div class="flex-container">
<div class="flex-item">They need content or a height/min-height</div>
<div class="flex-item">They need content or a height/min-height</div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<br>
<div class="flex-container2">
<div class="flex-item2"></div>
<div class="flex-item2"></div>
<div class="flex-item2"></div>
<div class="flex-item2"></div>
</div>

Using margin on flex items

I was under the impression that a margin can be added to flex items/children, and flexbox should automatically take that into account and calculate the correct spacing between the items.
I can't seem to get this working as I would like though.
Fiddle here: https://jsfiddle.net/dba5ehcw/1/
.flex-item{
border: 1px solid blue;
box-sizing: border-box;
height: 160px;
width: 50%;
}
So each flex item at the moment is half the width of the container, and they flow nicely next to each other.
I would like to be able to add a margin of say, 1em to the flex-items in order to give them some breathing room, but in doing so, they become larger than the 50% and no longer stack next to each other on the same line because they are too wide.
Is there a way to use margin on the flex-items and have the flexbox container take this into account and adjust (decrease) their widths accordingly?
There are multiple ways to do this:
Use calc:
.flex-item {
width: calc(50% - 2em);
margin: 1em;
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
border: 1px solid blue;
box-sizing: border-box;
height: calc(160px - 2em);
width: calc(50% - 2em);
margin: 1em;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
Use nested boxes:
.flex-item {
width: 50%;
display: flex;
}
.flex-item > div {
border: 1px solid blue;
flex: 1;
margin: 1em;
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
height: 160px;
width: 50%;
display: flex;
}
.flex-item > div {
border: 1px solid blue;
flex: 1;
margin: 1em;
}
<div class="flex-container">
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
</div>
Place each row in a nowrap container, and use a positive flex-shrink factor
.row {
display: flex;
}
.flex-item {
width: 50%;
margin: 1em;
}
.flex-container {
border: 1px solid red;
width: 320px;
}
.row {
height: 160px;
display: flex;
}
.flex-item {
border: 1px solid blue;
width: 50%;
margin: 1em;
}
<div class="flex-container">
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<div class="row">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
</div>
Don't use width. Instead, force line-breaks at the right places, and use flex: 1 to make the elements grow to fill remaining space.
.flex-item {
flex: 1;
}
.line-break {
width: 100%
}
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
border: 1px solid blue;
box-sizing: border-box;
height: calc(160px - 2em);
flex: 1;
margin: 1em;
}
.line-break {
width: 100%;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="line-break"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="line-break"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
You need to do it with padding - which, when in border-box mode does not make the container larger than it's specified width - not margin, and a nested flex div. This is how all flexbox-based grid systems work. Code below:
.flex-container{
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item{
padding:1em;
box-sizing: border-box;
height: 160px;
width: 50%;
display:flex;
}
.flex-item>div {
border: 1px solid blue;
flex: 1 1 auto;
}
<div class="flex-container">
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
<div class="flex-item"><div></div></div>
</div>
instead of using margins, try adding a gap on your flex container
.flex-container {
display: flex;
gap: 1em
}
flex-item {
width 50%
}
Try this : -
.flex-container {
border: 1px solid red;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
width: 320px;
}
.flex-item {
justify-content: space-around;
margin: 1%;
background: red;
border: 1px solid blue;
box-sizing: border-box;
height: 160px;
width: 48%;
}
<div class="flex-container">
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
I believe I was trying to achieve the same thing, from my understanding, with the addition that I wanted the two boxes to stack on top of each other when the viewport gets small enough (when viewing on mobile/tablet).
For some reason I thought this would be way easier as I saw something similar in a Bootstrap tutorial video I watched but I think he was using row and col classes, with a g gutter class, and not Flex.
Anyway, HTML:
<div class="d-flex flex-wrap flex-half-screen-responsive">
<div class="col-lg-6">
Lorem Ipsum
</div>
<div class="col-lg-6">
Lorem Ipsum
</div>
</div>
CSS:
.flex-half-screen-responsive {
margin: -0.5em;
}
.flex-half-screen-responsive > * {
flex: 1 1 48%;
margin: 0.5em;
}
I don't like how I have to specify that hardcoded 48% value but it seems to work just as I want it so whatever; spent way too much time on this already lol. Anyway I hope this helps someone looking for the same behavior.

Resources