Reputation: 19905
On a mobile layout, I have six divs one below another in order of importance. On desktop, I would like to reorder the items. On the image below, the mobile layout is on the left, the desktop layout on the right.
The heights of the six items are not related to each other. The layout should work with any heights.
I tried different solutions. The easiest to achieve the desktop layout would be to create two wrapper divs. But this does not allow to have a different order on mobile.
I tried to adapt forced horizontal wrapping in flexbox to vertical. This answer for column breaks almost worked, but it needs a fixed height for the container.
I searched about girds, and unfortunately, it seems impossible to have more than one element in a grid area.
Is there a way to solve this problem, without using Javascript ? (Our current solution involves Javascript, but the visitor can notice the rearrangement of the divs).
Upvotes: 2
Views: 2941
Reputation: 19905
@Paulie_D's comment and @G-Cyr's answer inspired me this solution:
The six div
s that must be aligned are grouped in two intermediate <div>
, representing the two columns in the desktop layout. They are grand-children of the container, instead of children.
On mobile, we ignore the column div
s with display: contents;
and reorder the grand-children using flexbox's order: n;
. On desktop we display the two columns using display: inline-block;
, and leave the grand-children in source-order.
for(let e of document.getElementsByClassName('item')) {
e.style.minHeight = Math.floor(25 + Math.random() * 72) + 'px';
}
.container {
display: flex;
flex-direction: column;
}
.item {
margin-bottom: 0.5em;
text-align: center;
background:#ddd;
padding: 1em;
}
.left, .right{
display: contents;
}
.item1, .item2{
order: 1;
}
.item3, .item4{
order: 2;
}
.item5, .item6{
order: 3;
}
@media (min-width: 40em){
.left, .right{
display: inline-block;
vertical-align: top;
}
.container{
display: block;
}
.left{
width: calc(67% - 0.5em);
margin-right: 0.5em;
}
.right{
width: 33%;
}
}
<div class="container">
<div class="left">
<div class="item item1">1</div>
<div class="item item3">3</div>
<div class="item item5">5</div>
</div><div class="right">
<div class="item item2">2</div>
<div class="item item4">4</div>
<div class="item item6">6</div>
</div>
</div>
Upvotes: 2
Reputation: 105893
flex
requires an height to wrap columns, (see last snippet demo using javascript to set the height required)
grid
requires to set spanning to inbricate elements,
column
CSS do not understand order
...
... Maybe there is float
left over here
// set a random min-height on children
for(let e of document.getElementsByClassName('element')) {
e.style.minHeight = Math.floor(25 + Math.random() * 72) + 'px';
}
* {
margin: 0;
box-sizing: border-box;
}
/* reset breakpoint to your own value */
@media screen and (min-width: 568px) {
div {
width: 50%;
}
div:nth-child(odd) {
float: left;
clear: left;
}
div:nth-child(even) {
overflow: hidden;
}
}
/* demo purpose */
section {
counter-reset: div;
}
div:before {
counter-increment: div;
content: "Div N°"counter(div);
}
div {
border: solid;
display:flex;
align-items:center;
justify-content:center;
}
<section>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</section>
Even then , gaps appears in between elements, float always breaks a line when following a non floatting element.
The masonry script could be an option, or javascript to sort out the height , so flex column could be used aside order.
Here is the javascript idea:
hl = 0;
hr = 0;
nb = 0;
for (let e of document.getElementsByClassName("element")) {
nb++;
h = Math.floor(50 + Math.random() * 50);
e.style.height = h + "px";
if (nb % 2 === 0) {
hr = hr + h;
} else {
hl = hl + h;
}
if (hl > hr) {
document.getElementById("test").style.height = hl + "px";
} else {
document.getElementById("test").style.height = hr + "px";
}
}
* {
margin:0;
box-sizing:border-box;
}
div {border:solid;box-sizing:border-box;width:25%;background:tomato;}
/* demo purpose */
section {
counter-reset: div;
background:gray;
display:flex;
flex-direction:column ;
flex-wrap:wrap;
}
div:nth-child(even) {
order:2;
width:75%;
background:orange
}
div:before {
display:block;
counter-increment: div;
content: "Div N°"counter(div);
}
@media screen and (max-width:568px) {
section {
height:auto!important;/* because of js script */
display:block;
}
section div:nth-child(1n) {width:auto;}
}
<section id="test">
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
</section>
Upvotes: 2
Reputation: 9136
RESULT
I'v used CSS Grids for this problem.
HTML
<div class="list">
<div class="item one">1</div>
<div class="item two">2</div>
<div class="item three">3</div>
<div class="item four">4</div>
<div class="item five">5</div>
<div class="item six">6</div>
</div>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Verdana';
color: #fff;
}
.list {
background: gold;
display: grid;
grid-gap: 10px;
text-align: center;
grid-template-columns: repeat(1, 1fr);
grid-auto-rows: minmax(70px, auto);
grid-template-areas:
"one"
"two"
"three"
"three"
"four"
"five"
"six";
}
.item {
background: gray
}
.one {
grid-area: one;
}
.two {
grid-area: two;
}
.three {
grid-area: three;
}
.four {
grid-area: four;
}
.five {
grid-area: five;
}
.six {
grid-area: six;
}
@media screen and (min-width: 650px) {
.list {
grid-template-columns: repeat(2, 1fr);
grid-template-areas:
"one two"
"one two"
"one four"
"three four"
"three six"
"three six"
"three six"
"five six"
"five .";
}
}
Upvotes: 2