Flexbox wrap, choose what child drops to next line - html

It's possible to choose what child of flexbox should drop to the next line, instead of always dropping the last one?
Example:
Full-Size, no breaking:
Default behavior, breaking the last one.
Desired behavior, breaking the div from the middle to the other line:
Is this possible using flexbox?

column can approximate this in case you need equal width elements
.box {
column-width:max(30vw,150px);
}
.box > div {
padding:10px;
width:100%;
display:inline-block;
border:1px solid;
margin-bottom:5px;
box-sizing:border-box;
}
/* I need those extra elements to have correct layout */
.box > i {
display:block;
}
<div class="box">
<div>A</div><i></i>
<div>B</div><i></i>
<div>C</div>
</div>

Edited: You can use the order: n; property to order flexed items and flex shorthand property to make b element full width with flex: 0 0 100% and then change the order to order: 2
.flexbox {
display: flex;
flex-wrap: wrap;
}
.a, .b, .c {
flex: 1;
}
#media(max-width: 768px) {
.b {
flex: 0 0 100%;
order: 2;
}
}
<div class="flexbox">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>

Related

Flex order not working as expected

I have a nav element with 3 child divs, with widths 25%, 50%, and 25% respectively, also flexbox order 1,2 and 3 respectively. In mobile view, I want to shift the second element down to next row taking full-width and first and third to stay in the first row consuming 50% widths each. I changed the second element's order to 3 and width 100%, also third element's order to 2 and 50% width. However, it is still not working as expected. Is it possible to achieve something like that using flexbox?
I have included a sample code
<nav>
<div class="a">
A
</div>
<div class="b">
B
</div>
<div class="c">
C
</div>
</nav>
and CSS
nav{
display:flex;
flex-direction:row;
}
.a,.b,.c{
border:1px solid blue;
text-align: center;
}
.a{
width:25%;
order:1;
}
.b{
width:50%;
order:2;
}
.c{
width:25%;
order:3;
}
#media screen and (max-width: 400px){
.a{
width:50%;
}
.b{
width:100%;
order:3;
}
.c{
width:50%;
order:2;
}
}
or use the fiddle
basically this is what I'm trying to achieve.
default view
on mobile
I updated your fiddle: https://jsfiddle.net/s9v926g9/1/
There are 2 things you need to add:
Add flex-wrap: wrap; to the nav element
This will make the elements wrap to a next line, instead of spacing themselves out over the available width.
Set the box sizing
Adding
*{
box-sizing: border-box;
}
to your css will make sure the borders are not messing up your flex-wrap.
You need to set flex-wrap: wrap on flex container and then you can just change order on mobile size and set flex: 0 0 100% on b element. Demo
nav {
display: flex;
flex-wrap: wrap;
}
.a, .b, .c {
border: 1px solid blue;
text-align: center;
}
.a, .c {
flex: 1;
}
.b {
flex: 2;
}
#media screen and (max-width: 400px) {
.b {
order: 2;
flex: 0 0 100%;
}
}
<nav>
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</nav>

Display First DIV beneath Second DIV

say I have...
<div id="A"></div>
<div id="B"></div>
How can the end-user view div B on top of div A on their browser?
I'm trying to do this with CSS without editing the html.
You can use flex-box and order to acheive what you want
body {
display: flex;
flex-wrap:wrap;
}
#A {
width: 100%;
height:50px;
background: red;
color:white;
order: 2;
}
#B {
width: 100%;
height:50px;
background: black;
color:white;
order: 1;
}
<div id="A">A</div>
<div id="B">B</div>
You need to add display:flex; and flex-direction:column-reverse; to the parent of your two divs.
body{
display:flex;
flex-direction:column-reverse;
}
Or you can choose div's order manually with order property:
body {
display: flex;
}
#A {
order: 2;
}
#B {
order: 1;
}
Use CSS3 flex to change the positioning of flex elements and this can be done using order property,
The CSS order property specifies the order used to lay out flex items
in their flex container.
#box{
display:flex;
width:100%;
flex-direction:column;
}
.A{
order:2;
background:#111;
color:#fff;
}
.B{
order:1;
background:#111;
color:#fff;
}
<div id="box">
<div class="A">A</div>
<div class="B">B</div>
</div>
A way to do this in CSS:
The container must have display:flex attribute
Then :
#A{
order:2;
}
#B{
order:1;
}
You can also achieve this with jQuery
$('#B:parent').each(function () {
$(this).insertBefore($(this).prev('#A'));
});
It feels unclean to do it this way but here you go (no container element needed)
#A {
display: table-footer-group;
}
#B {
display: table-header-group;
}
<div id="A">A</div>
<div id="B">B</div>

How to make inline-flex not affect one of the child

I have an HTML here.
<style>
.parent {
display: inline-flex;
}
</style>
<div class="parent">
<div class="child1"></div>
<div class="child2"></div>
<div class="image"><div>
</div>
Here I wanted child1 and image div's to come in one line so I put it inside parent div with property inline flex. But at the same time I don't want child1 and child2 divs to to come in the same line. Any solution available for that?
One method would be to enable wrap on the flex container.
Then make div.child2 appear as the last flex item (order: 1) and force it to wrap by setting a big enough width.
You can adjust the widths as you see fit.
.parent {
display: flex;
flex-wrap: wrap;
}
.child1, .image {
flex: 0 0 50%;
}
.child2 {
flex: 0 0 100%;
order: 1;
}
* { box-sizing: border-box; }
<div class="parent">
<div class="child1"></div>
<div class="child2"></div>
<div class="image"></div>
</div>
Using flex box, the most effective way I can see doing this is with this:
.parent {
display: inline-flex;
flex-wrap: wrap;
}
.child1,
.child2,
.image {
flex-grow: 1;
flex-basis: 50%;
}
.child2 {
order: 1;
}
/*DEMO STYLES ONLY*/
.parent {
height: 6em;
width: 18em;
background: rgba(90, 90, 90, .2)
}
.parent>div {
height: 2.5em;
outline: 1px solid red;
}
[class] {
position: relative;
}
[class]::after {
color: rgba(65, 65, 65, .8);
content: attr(class);
position: absolute;
bottom: 0;
right: 0;
}
<div class="parent">
<div class="child1"></div>
<div class="child2"></div>
<div class="image"><div>
</div>
The noteworthy properties are
flex-wrap which allows you to have more than one row
order which allows you to put an item at the start of what will be flex ordered (none of the other items get order, so they stay where they are)
flex-basis which tells the items at what point they should start growing
flex-grow which is optional, but if you choose it, will have the item on the second row fill up all available space
Put your children element in rows
.row { height:100px; overflow hidden;} // assuiming 100px is height of a child
.row > .children { height:100px; float:left;}
or assuming all div's are the same size oyu could enclose each pair of div [1][2][3][4] and float them to wherever you want.

Keeping all elements aligned to the same grid and spanning multiple columns

I'm trying out CSS3's flexbox for the first time and it seems promising, but I'm having some trouble getting it to behave.
Basically, I want flexbox to behave like a table but with an order property so I can tell it in the CSS what order to display the elements in the grid. Is this even possible with flexbox?
Here's the test page I created: https://jsfiddle.net/Lb838dwf/
HTML:
<div id="main">
<div id="div1">div 1</div>
<div id="div2">div 2</div>
<div>generic</div>
<div id="div3">div 3</div>
<div>generic</div>
<div id="div4">div 4</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
<div>generic</div>
</div>
CSS:
#main {
width: 100%;
border: 1px solid #c3c3c3;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
#main div {
min-width: 25%;
height: 100px;
-webkit-flex: 1;
flex: 1;
-webkit-order: 2;
order: 1;
background-color: #ccc;
}
#main #div1 {
-webkit-order: 3;
order: 3;
background-color: coral;
}
#main #div2 {
-webkit-order: 5;
order: 5;
background-color: lightblue;
}
#main #div3 {
-webkit-flex: 2;
flex: 2;
-webkit-order: 2;
order: 2;
background-color: lightgreen;
}
#main #div4 {
-webkit-order: 4;
order: 4;
background-color: pink;
}
You can see that #div3 has flex: 2, which means it should span two columns, but it's only taking up 1 column. Also, the 3 divs at the bottom (div1, div4, div2) aren't aligning to the same grid as the items above it. If I add max-width: 25% to the #main div style, it keeps everything to the same grid, but spanning columns doesn't work. I tried setting max-width: 25% for all divs and max-width: none for just #div3 (the one with flex:2) but that doesn't have an affect.
Let's dissect a few of the things you wrote so we could clarify flexbox behavior.
You can see that #div3 has flex: 2, which means it should span two
columns, but it's only taking up 1 column.
flex: 2 does not mean it should span two columns. This isn't like an HTML colspan attribute. (And even if it was, you have three other flex items ["table cells"] already occupying the three remaining columns, so how would #div3 expand two columns? It would have to break out of the grid.)
The flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.
The flex-grow property controls how flex items will expand by distributing remaining space in the flex container. So by applying flex: 2 you're saying you want the flex item to take twice as much remaining space than its siblings, not necessarily be double their size.
From the spec, the flex-grow property:
...determines how much the flex item will grow relative to the rest of
the flex items in the flex container when positive free space is
distributed.
However, since you've given the container (#main) a width: 100%, and each flex item min-width: 25%, there is no remaining space to distribute. So nothing happens.
To illustrate this behavior, change the width of each flex item to 50px. This leaves extra space to distribute and #div3 takes 2x as much. See demo: https://jsfiddle.net/Lb838dwf/6/
Also, the 3 divs at the bottom (div1,div4,div2) aren't aligning to
the same grid as the items above it.
Correct. They're not aligning because you applied flex: 1 to them in #main div. This tells them to evenly distribute all remaining space among themselves.
If I add max-width: 25% to the #main div style, it keeps
everything to the same grid, but spanning columns doesn't work. I
tried setting max-width: 25% for all divs and max-width: none for
just #div3 (the one with flex:2) but that doesn't have an affect.
What? You lost me.
I want flexbox to behave like a table but with an order property so I
can tell it in the CSS what order to display the elements in the grid.
Is this even possible with flexbox?
Yes, it's possible.
HTML
<div id="main">
<div>div 1</div>
<div>div 2</div>
<div>div 3</div>
<div>div 4</div>
<div>div 5</div>
<div>div 6</div>
<div>div 7</div>
<div>div 8</div>
<div>div 9</div>
<div>div 10</div>
<div>div 11</div>
</div>
CSS
#main {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
width: 100%;
}
#main div {
flex: 0 0 150px; /* don't grow, don't shrink, stay at 150px */
height: 100px;
margin: 5px;
background-color: #ccc;
border: 1px dashed black;
}
#main div:nth-child(2) { order: -4; background-color: lightblue; }
#main div:nth-child(5) { order: -3; background-color: lightgreen; }
#main div:nth-child(8) { order: -2; background-color: lightyellow; }
#main div:nth-child(11) { order: -1; background-color: lightpink; }
DEMO: http://jsfiddle.net/Lb838dwf/4/
Spanning multiple columns
As mentioned above, the flex property is a shorthand for the flex-grow, flex-shrink and flex-basis properties.
flex-grow tells flex items how to distribute available space, hence it is not a reliable tool for emulating the HTML colspan attribute. (The space available in the container and double the size of a flex item are unrelated lengths and not necessarily equal.)
However, the flex-basis property, which sets the initial size of a flex item, can be used to make flex items twice as wide, three times as wide, whatever.
In my code above, flex items are set to: flex: 0 0 150px; The third value represents flex-basis. So each box is set to 150px wide.
For a flex item to occupy two columns simply double that value.
Since flex cascades to all divs, we only need to adjust the flex-basis for targeted items.
#main div:nth-child(11) { flex-basis: calc(300px + 10px); } /* factoring in margin space */
#main div:nth-child(7) { flex-basis: calc(450px + 20px); } /* factoring in margin space */
DEMO: http://jsfiddle.net/Lb838dwf/5/ (expand window for effect)

CSS3 flexbox layout max 3 child items on one line

Is their an simple way in CSS to have a fixed maximum of child items on the same line, before you push the next child elements to a new line?
As i understand flexbox, child items only get pushed to a new line if their isint enough available space on the line above it. But i am seeking a CSS rule or function that let me say "i want a maximum of 3 child items on any given line, and even if space is available for a 4'th one push it to a new line".
Use flex-basis.
.child {
flex-basis: 33%;
}
The percentage must be adapted according to you box-sizing model, and the use of margins and/or padding.
Or you could use CSS Grid for this:
Your HTML:
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
Your CSS:
.parent {
display: grid; // activate grid
grid-template-columns: repeat(4, 1fr); //make 4 cols with size 1fr
grid-gap: 20px; //gap between the rows
}
.child { //thats written with less. Just unnest for vanilla css
&:nth-child(3n+1) {
grid-column: 1;
}
&:nth-child(3n+2) {
grid-column: 2;
}
&:nth-child(3n+3) {
grid-column: 3;
}
&:nth-child(3n+4) {
grid-column: 1; //put the fourth item in a new row
}
}
I'm sure there are more efficient ways to write this with grid. But this does the job.
Instead of using display: flex you could use float: left and clear every 3rd child node like this:
.child {
background: #000;
height: 300px;
float: left;
margin:15px 0 0 15px;
width:150px;
}
.child:nth-child(3n+1) {
clear: left;
}
I created a fiddle for you: fiddle example
In the case that the parent can hold only two children, you could use this short jQuery fix:
var child = $('.child'),
parent = $('.child').parent();
if( child.width() > (parent.width()/3) ) {
child.css('clear', 'none');
}
Fiddle with fix: fiddle example2
fiddle
#container {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
#container>div {
margin: 15px;
width: 150px;
height: 150px;
}
/* negative paddings are treated as 0 so woohoo */
#container>div {
/* up to 3 boxes in a row */
padding: 0 calc((100% - 180px * 3) / 6);
}
#container>div {
/* up to 4 boxes in a row */
//padding: 0 calc((100% - 180px * 4) / 8);
}
#container>div {
/* up to 5 boxes in a row */
//padding: 0 calc((100% - 180px * 5) / 10);
}
/* 180px = width + horizontal margins */
You could place the items inside a container div that has a width of 100% and a max-width that is just enough to fit three items inside it?
.parent {
width:100%;
max-width:350px;
}
And then place this around all the items.
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
If you are using it with bootstrap you shoul add this css for pseudo row class
.row:before,
.row:after{
width:0 !important;
}