41

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
ismriv
  • 933
  • 1
  • 10
  • 20

6 Answers6

27

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.

cimmanon
  • 67,211
  • 17
  • 165
  • 171
  • 2
    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! – ismriv Feb 23 '13 at 01:51
  • 2
    Personally I get real tired of the CSS standards needing to recreate newspaper/magazine layouts instead of allowing for more versatile layouts. – Gavin Jun 02 '22 at 09:09
  • 3
    Almost 10 years later and still no good solution for this unfortunately... – Loosie94 Sep 15 '22 at 09:40
6

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

JustCarty
  • 3,839
  • 5
  • 31
  • 51
David
  • 10,435
  • 3
  • 17
  • 11
  • 1
    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. – cimmanon Feb 17 '13 at 20:18
  • @cimmanon 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. – David Feb 17 '13 at 20:30
  • nice one...appreciate it! – Elon Zito Mar 08 '17 at 18:46
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;
        }
    }
}
  • 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. – FluffyKitten Sep 15 '20 at 01:42
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.

VakoShvili
  • 86
  • 1
  • 5
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]);
}
Cesar
  • 130
  • 2
  • 7
-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/

Elisey
  • 15
  • 1