Flexbox scroll overflowing content in an dynamic sized parent - javascript

I have a simple menu and content div structure. The menu has no fixed size and can expand depending on its content. The content div underneath should take the available space and scroll its own content if overflowing. Unfortunately, flexbox now behaves in such a way that the content div, due to its flex:1 property, expands according to its content instead of scrolling the content.
Is there a way to preserve the dynamic sizes using flex:1 and also have the content of the content div scroll?
function toggleMenu() {
const menu = document.querySelector(".menu");
if(menu.classList.contains("open")) {
menu.querySelector(".text").innerHTML = "<p> small text </p>";
menu.classList.remove("open");
}else {
menu.querySelector(".text").innerHTML = "<h1> im the menu </h1>";
menu.classList.add("open");
}
}
body {
padding: 0;
margin: 0;
background-color: #17141d;
display: flex;
height: 100vh;
}
.main {
display: flex;
flex-direction: column;
flex: 1;
background-color: grey;
}
.menu {
background-color: blueviolet;
}
.content {
display: flex;
flex: 1;
background-color: aqua;
}
.segment-wrapper {
flex: 1;
display: flex;
background-color: red;
flex-direction: column;
overflow-y: scroll;
padding: 10px;
box-sizing: border-box;
}
.segment {
height: 500px;
background-color: green;
border-radius: 5px;
border: solid 1px black;
width: 100%;
margin-bottom: 10px;
}
<div class="main">
<div class="menu open">
<div class="text"><h1>im the menu</h1></div>
<button onclick="toggleMenu()">Toggle</button>
</div>
<div class="content">
<div class="segment-wrapper">
<div class="segment">
</div>
<div class="segment">
</div>
<div class="segment">
</div>
</div>
</div>
</div>

you are missing
an height on .main to size it according to body or the viewport's height.
overflow on .content, so it overflows, unless you want .main to overflow (which body does already)
and finally flex-shrink:0; on .segment so it doesn't shrink :)
here an example of what i understood you were after:
function toggleMenu() {
const menu = document.querySelector(".menu");
if(menu.classList.contains("open")) {
menu.querySelector(".text").innerHTML = "<p> small text </p>";
menu.classList.remove("open");
}else {
menu.querySelector(".text").innerHTML = "<h1> im the menu </h1>";
menu.classList.add("open");
}
}
body {
padding: 0;
margin: 0;
background-color: #17141d;
display: flex;
height: 100vh;
}
.main {
display: flex;
flex-direction: column;
flex: 1;
background-color: grey;
max-height;100%;
}
.menu {
background-color: blueviolet;
}
.content {
display: flex;
flex: 1;
background-color: aqua;
overflow:auto;
}
.segment-wrapper {
flex: 1;
display: flex;
background-color: red;
flex-direction: column;
overflow-y: scroll;
padding: 10px;
box-sizing: border-box;
}
.segment {
flex-shrink:0;
height: 500px;
background-color: green;
border-radius: 5px;
border: solid 1px black;
width: 100%;
margin-bottom: 10px;
}
<div class="main">
<div class="menu open">
<div class="text"><h1>im the menu</h1></div>
<button onclick="toggleMenu()">Toggle</button>
</div>
<div class="content">
<div class="segment-wrapper">
<div class="segment">
</div>
<div class="segment">
</div>
<div class="segment">
</div>
</div>
</div>
</div>

Related

How can I change 2 column layout to a tabbed layout for tablet and mobile

Hi I am updating an old website for a Spanish Cafe. At the moment it uses a 2 column layout for desktop, then a smaller version for tablets and finally a tabbed layout for mobile users:
as this involves 3 layouts there is a lot of repetitive text. What I would like to do is have a responsive design changing from 2 column to tabbed through breakpoints.
Is this possible? I have googled everywhere but cannot find anything.
If anyone should wonder the same thing, here is how I managed to do it (just an example of code).
At the top I hide a div with 2 tabs by display:none; then I have a flex-container with 2 flex items, left is in Spanish and right in English.
Both display side by side until the viewport gets to 600px the the media-query hides all the english flex items and shows the tab div.
With a bit of javascript the user can toggle between Spanish and English
HTML
<br>
<div class="wrap">
<div class="tabh" style="margin:0 auto;">
<div id="esp" class="atab" onclick="func('a')">EspaƱol</div>
<div id="eng" class="atab" onclick="func2('b')">English</div>
</div>
<div class="flex-container">
<div class="flexitem spanish">Spanish</div>
<div class="flexitem english">English</div>
</div>
<div class="flex-container">
<div class="flexitem spanish">Spanish</div>
<div class="flexitem english">English</div>
</div>
<div class="flex-container">
<div class="flexitem spanish">Spanish</div>
<div class="flexitem english">English</div>
</div>
</div>
CSS
.wrap {
width: 900px;
max-width: 100%;
margin: 0 auto;
}
.flex-container {
width: 100%;
display: flex;
margin: 0 auto;
flex-direction: row;
justify-content: space-between;
font-family: Roboto;
font-weight: bolder;
color: white;
}
.flexitem {
text-align: left;
width: 50%;
padding: 0 15px;
}
.tabh {
width: 90%;
margin-left: auto;
margin-right: auto;
display: none;
text-align: center;
font-family: Roboto;
font-weight: bolder;
margin-bottom: 10px;
}
.atab {
width: 50%;
height: 25px;
display: table-cell;
border-radius: 10px;
cursor: pointer;
padding-top: 4px;
border: 1px solid black;
}
.spanish {
background-color: red;
height: 100px;
margin: 5px;
text-align: center;
}
.english {
background-color: blue;
height: 100px;
margin: 5px;
text-align: center;
}
#media only screen and (max-width: 600px) {
.english {
display: none;
width: 100%;
}
.spanish {
width: 100%;
}
.tabh {
display: flex;
}
.flex-container {
flex-direction: column;
}
}
#media only screen and (min-width: 601px) {
.spanish {
display: block !important;
width: 50%;
}
.english {
display: block;
width: 50%;
}
.tabh {
display: none;
}
}
javascript
function func(a) {
const esps = document.querySelectorAll('.spanish');
const engs = document.querySelectorAll('.english');
esps.forEach(esp => {
esp.style.display = "block";
});
engs.forEach(eng => {
eng.style.display = "none";
});
}
function func2(b) {
const esps = document.querySelectorAll('.spanish');
const engs = document.querySelectorAll('.english');
esps.forEach(esp => {
esp.style.display = "none";
});
engs.forEach(eng => {
eng.style.display = "block";
});
}
A working example is here jsfiddle
I hope this helps someone.

How to create 3 divs side by side using flex where middle div can (grow&shrink) in respect to other two divs?

How to create 3 divs side by side using flex where middle div can (grow&shrink) in respect to other two divs?
I want to create Three divs A B C with a parent containing flex css property. Having A fixed width of 300px and C having min width 200px and can grow in width as more content is added.
While B should be in between A and C should grow and shrink automatically as per the widths of A and C.
Although A and C can be of any width 300px and 200px are just examples.
I've tried but unable to place it correctly.
For C i used flex: 1 1 0%; with A and C with fixed width - But didn't worked
You can give childB, You can read more about flex here
flex: 1;
.parent {
display: flex;
}
.child {
height: 300px;
display: flex;
justify-content: center;
align-items: center;
}
.childA {
flex-basis: 300px;
background-color: turquoise;
}
.childB {
flex: 1;
background-color: wheat;
}
.childC {
flex-basis: 200px;
background-color: blueviolet;
}
<div class="parent">
<div class="child childA">A</div>
<div class="child childB">B</div>
<div class="child childC">C</div>
</div>
it will be easier if you write your own code, which does not work
.abc {
display: flex;
height: 200px;
width: 400px;
margin: 0 auto;
border: 1px solid black;
}
.A {
min-width: 150px;
flex-shrink: 0;
background-color: aliceblue;
}
.B {
flex-grow: 1;
background-color: grey;
}
.C {
min-width: 100px;
flex-shrink: 0;
background-color: antiquewhite;
}
<div class="abc">
<div class="A"></div>
<div class="B"></div>
<div class="C"></div>
</div>
HTML :
<div class="container">
<div class="box">A</div>
<div class="box">B</div>
<div class="box">C</div>
</div>
CSS :
.container {
display: flex;
flex-wrap: nowrap;
border: 1px solid red;
height: 100px;
}
.box {
border: 1px solid black;
margin: 5px 5px 5px 5px;
}
.box:nth-child(2) {
width:25%;
background-color: blue;
}
you can use nth-child(child) to select any child and costom it
Result : https://codepen.io/pk-salma/pen/qBjqwBG

How to fit the `main` html elment if `aside` is not present?

I want this only with CSS-GRID
I want to fit the main element to full width if aside is not present.
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-size: 2rem;
}
div.root {
height: 100%;
background-color: blueviolet;
display: grid;
grid-template-columns: 200px auto;
}
aside {
background-color: aquamarine;
}
main {
background-color: burlywood;
}
<div class="root">
<aside> aside </aside>
<main> main </main>
</div>
I've search on the SOF and found the following but It didn't work STACKOVERFLOW
What I've trid is to give the grid-template-columns as auto auto and add the width to the aside but then main doesn't stretch to its full width.
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-size: 2rem;
}
div.root {
height: 100%;
background-color: blueviolet;
display: grid;
grid-template-columns: auto auto;
}
aside {
width: 200px;
background-color: aquamarine;
}
main {
background-color: burlywood;
}
<div class="root">
<!-- <aside> aside </aside> -->
<main> main </main>
</div>
You could use the + selector in CSS, though this only works if your aside is on the left.
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-size: 2rem;
}
div.root {
height: 100%;
background-color: blueviolet;
display: grid;
grid-template-columns: 200px auto;
}
aside {
background-color: aquamarine;
grid-column-start: span 1;
}
main {
background-color: burlywood;
grid-column-start: span 2;
}
aside + main {
grid-column-start: span 1;
}
<div class="root">
<!--<aside> aside </aside>-->
<main> main </main>
</div>
<br/><br/>
<div class="root">
<aside> aside </aside>
<main> main </main>
</div>
Use display:flex;
html,
body {
height: 100%;
margin: 0;
padding: 0;
font-size: 2rem;
}
div.root {
height: 100%;
background-color: blueviolet;
display:flex;
}
aside {
background-color: aquamarine;
min-width:200px;
max-width:200px;
}
main {
background-color: burlywood;
flex:1;
}
<div class="root">
<!-- <aside> aside </aside> -->
<main> main </main>
</div>

How to control the height of descendant box within a nested flexbox context?

.main {
height: 100px;
width: 100px;
border: 1px solid;
background: green;
}
.columns {
display: flex;
}
.columns.vertical {
flex-direction: column;
flex-wrap: nowrap;
}
.box22 {
overflow: auto;
}
.box1 {
background: red;
}
.box2 {
background: orange;
border: 1px blue solid;
}
.box222 {
background: white;
border: 1px blue solid;
}
<md-content class="md-padding">
<div class="main columns vertical">
<nav class="box1">nav</nav>
<div class="box columns vertical">
<div class="b">asdasd</div>
<div class="box2 columns vertical">
<div class="box21 columns">
box21
</div>
<div class="box22 columns">
<div class="box221">box221</div>
<div class="box222">
<p>a</p>
<p>a</p>
</div>
</div>
</div>
</div>
</div>
</md-content>
I manually set the height of outermost box main and also set box22 with overflow:auto, however the box22 stretched out the main box and no scroll bar shows up.
What I want is to set height in the outermost box and the nested inner box can automitically control it's height thus all boxes are fitted. How to fix these kind of problem, should I set heights for all descendant boxes?
I tested setting overflow :auto at box(level1), box2(level2) and box22(level3), only box(level1) can automatically control it's height and shows up a scroll bar when it's height overlayed.
what I want is shown below, however, to achieve this, I need to manually specify the father box of box22. In other words, if I want to make a flex contaner scrollable, I have to at least set it's father box's height, which is toally unaccptable.
.main {
height: 100px;
width: 100px;
border: 1px solid;
background: green;
}
.columns {
display: flex;
}
.columns.vertical {
flex-direction: column;
flex-wrap: nowrap;
}
.box22 {
overflow: auto;
}
.box1 {
background: red;
}
.box2 {
height:64px;
background: orange;
border: 1px blue solid;
}
.box21 {
background: yellow;
}
.box222 {
background: white;
border: 1px blue solid;
margin-top: auto;
}
<md-content class="md-padding">
<div class="main columns vertical">
<nav class="box1">nav</nav>
<div class="box columns vertical">
<div class="b">asdasd</div>
<div class="box2 columns vertical">
<div class="box21 columns">
box21
</div>
<div class="box22 columns">
<div class="box221">box221</div>
<div class="box222">
<p>a</p>
<p>a</p>
<p>a</p>
</div>
</div>
</div>
</div>
</div>
</md-content>
Well, as you noted, the parent container needs a height declaration in order to generate a scrollbar.
This makes sense because there's no way to trigger an overflow condition if there's no fixed length.
With a flexible length (e.g., height: auto), the container will shrink and expand to accommodate content, never triggering an overflow and, therefore, never generating a scrollbar.
MDN describes it like this:
In order for overflow to have an effect, the block-level container must have either a set height (height or max-height) or white-space set to nowrap.
So that's your obstacle. Here's how you get around it (knowing that you can't set more heights):
Give the browser what it needs to trigger the overflow: set a tiny (1px) height.
You get the full height that you want: use flex-grow to consume remaining space.
So something like this...
height: 1px;
flex-grow: 1;
but more efficiently:
flex: 1 0 1px; /* fg, fs, fb */
Add this to your code:
.main .columns.vertical {
flex: 1 0 1px;
}
.box22 {
flex: 1 0 1px;
}
.main {
height: 100px;
width: 100px;
border: 1px solid;
background: green;
}
/* NEW */
.main .columns.vertical {
flex: 1 0 1px;
}
/* ADJUSTMENT */
.box22 {
flex: 1 0 1px;
overflow: auto;
}
.columns {
display: flex;
}
.columns.vertical {
flex-direction: column;
flex-wrap: nowrap;
}
.box22 {
flex: 1 0 1px;
overflow: auto;
}
.box1 {
background: red;
}
.box2 {
background: orange;
border: 1px blue solid;
}
.box222 {
background: white;
border: 1px blue solid;
}
<md-content class="md-padding">
<div class="main columns vertical">
<nav class="box1">nav</nav>
<div class="box columns vertical">
<div class="b">asdasd</div>
<div class="box2 columns vertical">
<div class="box21 columns">box21</div>
<div class="box22 columns">
<div class="box221">box221</div>
<div class="box222">
<p>a</p>
<p>a</p>
</div>
</div>
</div>
</div>
</div>
</md-content>
consider using max-height instead of height on main also... if you start with flexbox... best to keep display: flex through the whole chain
.top {
position: static;
height: 200px;
width: 200px;
max-height: 200px;
max-width: 200px;
overflow: auto;
}
.flexcol {
width: 100%;
display: flex;
flex-direction: column;
padding: 0;
}
.mynav {
display: flex;
flex-direction: row;
flex-grow: 1;
background-color: blue;
}
.box1 {
display: flex;
flex-grow: 1;
background-color: red;
}
.box2 {
display: flex;
flex-direction: column;
flex-grow: 1;
background-color: green;
}
.box3 {
display: flex;
flex-grow: 1;
background-color: yellow;
}
.box221 {
min-height: 50px;
background-color: pink;
width: 100%;
}
.box222 {
min-height: 50px;
background-color: grey;
width: 100%;
}
.darklink {
color: white;
}
<div class="top">
<div class="flexcol">
<nav class="mynav">
<a class="darklink" href="#" target="_">Link1</a>
<a class="darklink" href="#" target="_">Link2</a>
<a class="darklink" href="#" target="_">Link3</a>
</nav>
<div class="box1">
<p>para</p>
</div>
<div class="box2">
<div class="box221">
<p>para</p>
<p>para</p>
</div>
<div class="box222">
<p>para</p>
<p>para</p>
<p>para</p>
</div>
</div>
<div class="box3">
<p>para</p>
</div>
</div>
</div>

How to set div scrollable when content more than size of the page?

I have the next page:
<div id = "menu">
Menu on the left side
</div>
<div id = "header">
Header content of the page
</div>
<div id = "body">
Data Data Data Data Data Data Data
</div>
<div id = "footer">
Additional Information
</div>
Whith Next layout: Menu should be on the left side:
#menu{
background: #244a7c;
padding: 7px 23px 0 7px;
width: 299px;
height: 1000px;
overflow: inherit;
margin-left: 0px;
display: block;
float: left;
}
#header{
display: inline-block;
width: 400px;
border-bottom: 1px solid rgb(238, 238, 238);
}
Body can have different data inside. My problem is:
When content of the body more than user page I want to fix all div except body. Menu should be on the left side, Header should be on the top of the page and footer on the bottom and ONLY body should be scrollable.
Any help, please.
Thanks!
Here's 2 Pure CSS solution
Without fixing any height (header/footer) or width (left column).
I actually prefer the second solution. (even tho he has less browser support)
1 - using CSS tricks
this is a totally responsive design and work well with all browsers (IE10, FF, Chrome, Safari, Opera, mobile browsers)
Working Fiddle
HTML:
<div class="Container">
<div class="Header">
</div>
<div class="HeightTaker">
<div class="Wrapper Container Inverse">
<div>
<div class="Footer">
</div>
</div>
<div class="HeightTaker">
<div class="Wrapper">
<div class="LeftMenu">
</div>
<div class="Content">
</div>
</div>
</div>
</div>
</div>
</div>
CSS:
*
{
margin: 0;
padding: 0;
}
html, body, .Container
{
height: 100%;
}
.Container:before
{
content: '';
height: 100%;
float: left;
}
.HeightTaker
{
position: relative;
z-index: 1;
}
.HeightTaker:after
{
content: '';
clear: both;
display: block;
}
.Wrapper
{
position: absolute;
width: 100%;
height: 100%;
}
.Inverse, .Inverse > *
{
-moz-transform: rotateX(180deg);
-ms-transform: rotateX(180deg);
-o-transform: rotate(180deg);
-webkit-transform: rotateX(180deg);
transform: rotateX(180deg);
}
.LeftMenu
{
height: 100%;
float: left;
}
.Content
{
overflow: auto;
height: 100%;
}
/*For demonstration only*/
p
{
font-size: 1.3em;
}
.Important
{
font-weight: bolder;
color: white;
}
body > .Container
{
text-align: center;
}
.Header
{
background-color: #bf5b5b;
}
.LeftMenu
{
background-color: #bdbe4c;
}
.Content
{
background-color: #90adc1;
}
.Footer
{
background-color: #b5a8b7;
}
2 - using Flex
This layout can also be achieved using flex, but the current browser support is pure.
Here's a Working Fiddle only FF,Chrome,IE10.
HTML: (simpler)
<header>
</header>
<section class="Middle">
<div class="LeftMenu">
</div>
<div class="Content">
</div>
</section>
<footer>
</footer>
CSS:
*
{
margin: 0;
padding: 0;
}
html, body
{
height: 100%;
text-align: center;
}
body
{
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
}
.Middle
{
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 1 0;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
overflow: hidden;
}
.Content
{
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 0 0;
overflow: auto;
}
/*For demonstration only*/
p
{
font-size: 1.3em;
}
.Important
{
font-weight: bolder;
color: white;
}
header
{
background-color: #bf5b5b;
}
.LeftMenu
{
background-color: #bdbe4c;
}
.Content
{
background-color: #90adc1;
}
footer
{
background-color: #b5a8b7;
}
If you set the header, footer & menu position as fixed & leave the body as it is, it should work. Only the body will be scrollable.
#header {
position: fixed;
top: 0;
display: inline-block;
width: 400px;
border-bottom: 1px solid rgb(238, 238, 238);
}
#footer {
position: fixed;
bottom: 0;
}
#menu {
position: fixed;
left: 0;
background: #244a7c;
padding: 7px 23px 0 7px;
width: 299px;
height: 1000px;
}
#body {
margin-left: 300px;
margin-top: <header-height>;
margin-bottom: <footer-height>;
}

Categories

Resources