CSS columns with left-right flow
Asked Answered
N

6

42

Let's say I have a div which will contain a set of elements (divs), which may have different heights, but all of them will have the same width.

I've achieved this currently with isotope + masonry, but since some browsers already support CSS3 multi-columns, I was hoping to have a only-CSS solution for these browsers, falling back to Javascript for the rest.

This is the CSS I've been trying:

.div-of-boxes {
  -webkit-column-count: 3;
  -webkit-column-gap:   10px;
  -moz-column-count:    3;
  -moz-column-gap:      10px;
  column-count:         3;
  column-gap:           10px;
}

However, this makes the flow of the elements to be top-down left-right. I'd like instead a left-right top-down flow. This is an example of what I'd like:

1 2 3
4 5 6
7 8 9

But this is what I get:

1 4 7
2 5 8
3 6 9

In Flow multi-column elements left-right before top-down something similar is asked, but I'm not satisfied with the answer, and it won't work with elements of different height. Is this possible at all with CSS columns, or is it a limitation?

Nogging answered 17/2, 2013 at 19:31 Comment(0)
S
28

The multi-column specification offers no property to change the distribution of elements among the columns: http://www.w3.org/TR/css3-multicol/. Such a property seems to go against what the module was designed for (recreating how newspaper or magazine articles are laid out).

None of the other pure CSS solutions will allow you to achieve the effect you are looking for.

Silurid answered 17/2, 2013 at 20:10 Comment(3)
This is what I was suspecting... I'll keep my javascript implementation, and fallback to CSS-only if javascript is not enabled, users will see some gaps between pictures, that'd be the trade-off. Thanks for confirming!Nogging
Personally I get real tired of the CSS standards needing to recreate newspaper/magazine layouts instead of allowing for more versatile layouts.Granddaughter
Almost 10 years later and still no good solution for this unfortunately...Choplogic
C
7

If your layout is always going to be 3 columns wide, you could try using the nth selector on your internal divs.
You could do this by clearing your 4th item.

#container {
    overflow: hidden;
    width: 440px;
}
#container div {
    background-color: gray;
    width: 110px;
    margin: 5px;
    float: left;
}
#container div:nth-child(4) {
    clear: both;
}
<div id="container">
    <div id="widget1">1</div>
    <div id="widget2">2</div>
    <div id="widget3">3</div>
    <div id="widget4">4</div>
    <div id="widget5">5</div>
    <div id="widget6">6</div>
    <div id="widget7">7</div>
    <div id="widget7">8</div>
</div>

JS Fiddle

Carvey answered 17/2, 2013 at 20:14 Comment(3)
If I understand the OP's question correctly (especially since they indicate they want to recreate Masonry with CSS), the problem is that element 2's height causes an unattractive gap between elements 1 and 4 as well as 3 and 6. Columns gives the effect they're looking for, just in the wrong order.Silurid
@Silurid You're right. I miss understood the question. I'll leave it just is case it's a viable alternative for someone else. I'll be watching to see any future answers that may come about.Carvey
nice one...appreciate it!Nudnik
N
0

Here's a link on solution! Used only flex.

html

- var n = 0;
ul
  while n < 40
    li= n++
  li

css

ul {
    list-style: none;
    padding: 30px;
    background: #28285e;
    
    li {
        padding: 20px;
        margin: 6px;
        background: white;
        width: calc(50% - 52px);
        height: 100px;
        display: inline-block;
        border: {
            radius: 4px
        }
        
        &:nth-child(3n) {
            height: 40px;
        }
        
            
        &:nth-child(2n + 1) {
            height: 80px;
        }
        
        &:nth-child(even) {
            float: right;
        }
        
        &:nth-child(odd) {
            float: left;
        }
        
        &:last-child {
            clear: both;
            float: none;
        }
    }
}
Nuthouse answered 14/9, 2020 at 18:22 Comment(2)
Welcome to Stack Overflow. Code-only answers are discouraged on Stack Overflow because they don't explain how it solves the problem. Please edit your answer to explain what this code does and how it answers the question, so that it is useful to other users with similar issues.Ulphiah
Hi Marian, can you please explain how this code works, including why there are no HTML tags in your html section?Asleyaslope
G
0

There's a CSS-only solution using flexbox, :nth-child(), and order as described in this blog post by Tobias Ahlin.

The idea is quite simple. flex-flow: column with wrap can do precisely what column-count does. You'll need two conditions to make it work: 1. The flexbox container needs to have a fixed height and it needs to be taller than your tallest column. 2. Flex children need width, 50% for 2-column layout, 33% for 3-column layout, etc.

.flex-container {
  display: flex;
  flex-flow: column wrap;
  height: 600px; /* You'll need to play with this value to distribute items correctly */
}

.flex-child {
  width: 33%; /* Creates 3-column layout */
}

This approach has the same problem as column-count: the elements are ordered by column, top-down. But since we're using flexbox, now we get access to the order property.

Using :nth-child() and order we can override the default order.

.flex-child:nth-child(3n+1) { order: 1; }
.flex-child:nth-child(3n+2) { order: 2; }
.flex-child:nth-child(3n)   { order: 3; }

Items with order: 1 will go in the first column, order: 2 will go in the second column, and order: 3 in the third column.

In (an + b) formula a represents a cycle size, n is a counter (starts at 0), and b is an offset value. So (3n+1) selects every third item starting with the first one. (3n+2) selects every third item but starting with the second item. And (3n) selects every third item starting with the third item, since there's nothing at index 0.

In certain layouts, columns might merge. To solve this issue, Tobias inserts pseudo-elements between columns:

/* Force new columns */
.flex-container::before,
.flex-container::after {
  content: "";
  flex-basis: 100%;
  width: 0;
  order: 2;
}

I won't explain how this works here, but you can read more about it here.

Here's a CodePen by Tobias for a 3-column layout. If you're working with more than three columns, read how to make adjustments here.

Glabella answered 19/1, 2021 at 17:50 Comment(1)
If the number of items is unknown, how do you want to set up the height of container?Janssen
K
0

There doesn't seem to be a built-in css only way to this. The most straightforward solution is to simply place N containers next to each other (flex, table) and then rearrange the list into the N containers in code by going through the items and pushing the next item into the next column like this:

const N = 3;
const columnsToView = [];
for (let columnIndex = 0; columnIndex < N; columnIndex++) {
  columnsToView[columnIndex] = [];
}

for (let itemIndex = 0; itemIndex < itemsToView.length; itemIndex++){
  columnsToView[itemIndex % N].push(itemsToView[itemIndex]);
}
Kheda answered 20/4, 2023 at 11:42 Comment(0)
H
-1

You can use jQuery to rearrange items in columns. In case of 2 cols it would be:

$('.your-columns-container .your-item:nth-child(even)').appendTo('.your-columns-container');

https://jsfiddle.net/hzvp7sgf/

And something more complicated for 3 columns:

$('.your-columns-container .your-item:nth-child(3n - 1)').addClass('container-col2');
$('.your-columns-container .your-item:nth-child(3n)').addClass('container-col3');

$('.container-col2').appendTo('.your-columns-container').removeClass('container-col2');
$('.container-col3').appendTo('.your-columns-container').removeClass('container-col3');

https://jsfiddle.net/d4LeLyu5/

Hexahydrate answered 18/8, 2015 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.