ismriv
ismriv

Reputation: 973

CSS columns with left-right flow

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?

Upvotes: 45

Views: 23273

Answers (6)

Cesar
Cesar

Reputation: 122

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]);
}

Upvotes: 0

VakoShvili
VakoShvili

Reputation: 86

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.

Upvotes: 0

Marian Kosaniak
Marian Kosaniak

Reputation: 191

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;
        }
    }
}

Upvotes: 0

David
David

Reputation: 10625

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

Upvotes: 7

Elisey
Elisey

Reputation: 15

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/

Upvotes: -1

cimmanon
cimmanon

Reputation: 68319

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.

Upvotes: 29

Related Questions