Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS: Floating multiple elements with different heights on multiple rows?

Tags:

html

css

rows

I'm trying to organize divs into two columns, but not force them into rows. I'm also trying to keep the vertical spacing between the divs a constant.

You can see the following demo, which would be correct if there wasn't huge amounts of vertical whitespace between the divs in each column.

html

<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>

I thought that I could just float them to the left with a static width, but apparently that didn't work.

Any ideas?

like image 240
qwerty Avatar asked Jan 11 '13 14:01

qwerty


3 Answers

First things first: you won’t be able to acheive a columnar wrapping layout with floats: as other commenters have pointed out, that’s just not how floats work. Nor is there an "automatic masonry-layout" module in CSS, which I suspect is what you’re really after. I also assume that placing each item individually is out of the question.

The closest thing you’ll get is Grid Layout—we’ll get to that towards the end. Spoiler: it’s really cool, but it’ll be a little while before browser support is really good.

So, TL;DR: there is no cross-browser method for doing a smart, row-by-row, masonry-like layout in CSS. But Grid Layout comes damn near. If you can't wait, use JS or switch the design to something that works with wrapping columns, for example.

If you choose the JS route, you could use the Grid Layout JS polyfill! That way, you can write grid layout syntax today, and remove the polyfill when it’s no longer needed, at some point in the future.


Columnar layout with multi-col

Judging from other answers and comments, a columnar wrapping layout is not what you want though. Just in case you still decide to go that route, you can do it with Multi Column layout. Support is pretty good: everything except IE<10, pretty much.

A quick example, just in case you want to change your layout to go in the multicol direction: here’s a JSBin with 2-column multicol layout. Note that you do not need to set both column-count and column-width: set a column-count (and column-gap) and the rest will sort itself out.

Oh, and if you get buggy rendering with margins (e.g. margins wrapping onto new columns, creating uneven layouts), the usual fix is to set display: inline-block; width: 100%; on the .module. Your milage may vary: for something that’s been around a long time, multicol sure is buggy.

Columnar layout with flexbox

Sticking with columnar layout for just a little bit longer, you can also use flexbox. (I see one other answer bringing up Flexbox, but sadly a completely outdated syntax which will not work in modern browsers).

The difference between wrapping into columns in multi-col vs flexbox is that flexbox does not balance the items into columns for you (it’s not really made for that type of layout), so you need to set a specific height in order to force the columns to wrap. If you have a container element (.container) and the .module items inside it, the following is the basic approach:

.container {
  display: flex;
  flex-flow: column wrap;
  height: 1000px; /* or whatever works for you */
}
.module {
  /* whatever props you want for modules */
}

...not counting prefixes and syntax variations for IE10, which uses a slightly older variation of the spec. (There’s also the old-old flexbox, which doesn’t support flex wrapping, so we’ll forget about that). Simple JSBin wrapping flexbox example here.

Dense packing with Grid Layout auto placement

Finally, we get to Grid Layout. This new spec has some really cool features, that allow us to do something close to a "Masonry" layout.

Heads up: you need to check these examples in Chrome Canary, WebKit Nightly or Firefox Developer edition. There is an old implementation of Grid Layout in IE10+ (and Edge as well), but it doesn’t contain all the new hotness properties we need.

First the markup: we’ll use a wrapper element + the .module items inside (the wrapper is not strictly necessary—we could have put the modules directly into body, but I just find it clearer this way):

<div class="container">
  <div class="module"></div>
  <div class="module"></div>
  <!-- and so on -->
</div>

Next, we tell the container to be a grid container:

.container {
  display: grid;
}

Then we set it to have a "template" for columns of 2 equal width columns (1 "fractional unit"), and gaps between columns as well as rows of 20px:

.container {
  display: grid;

  /* 2 columns, equally wide: */
  grid-template-columns: 1fr 1fr;

  /* gaps between each column and row. */
  grid-column-gap: 20px;
  grid-row-gap: 20px;
  /* ...can also be shortened to grid-gap: 20px; */
}

We have not yet given it a template for rows, but we only need to allow it to automatically create them as needed. For this, we set the grid-auto-rows property, to add as many 50px tall rows as needed:

.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-column-gap: 20px;
  grid-row-gap: 20px;

  /* automatically create rows, 50px tall each: */
  grid-auto-rows: 50px;
}

At this point, we have not placed any items inside the container yet, at least not explicitly. With grid, you can specify exactly which area of the grid an item shoud belong in. The thing is, you don’t have to! The grid will place them automatically in the first available slot, starting at the top left and going row by row by default, unless you tell it otherwise.

Instead, we need to tell it that some items are bigger than others. We don’t use sizes for this, but instead tell odd items to span 2 rows, and every 3rd item to span 4 rows.

.module {
  background: yellow;
}

.module:nth-child(odd) {
  grid-row: span 2;
  background: green;
}
.module:nth-child(3n) {
  grid-row: span 4;
  background: orange;
}

Finally, we need to tell the grid to use the "dense" algorithm, meaning it will make one placement pass per item, trying to avoid making "holes" in the auto placement.

.container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-column-gap: 20px;
  grid-row-gap: 20px;
  grid-auto-rows: 50px;

  /* use the "dense" algorithm: */
  grid-auto-flow: row dense;
}

This gives us something very close to a "masonry" layout, as can be seen in this JSBin example of a dense grid layout — Again, note that you will need nightly/dev versions of Chrome/WebKit or Firefox to see how it works.

Here’s an image of the result, as viewed in Chrome Canary:

grid layout in Chrome Canary

Some things to note:

  • The row dense packing algorithm doesn’t really make a difference here, as the items happen to stack up in a pattern that leaves no holes, but I put it in there for completeness.
  • As you can see, there’s a lone item on the last row—grid can’t magically resize things (like a jQuery Masonry plugin, for example), it can only size according to grid positions or spans. It's not a silver bullet for box-packing algorithmic layout. That said, it’s incredibly powerful and fun once you master it. Within a year or so (at the max, my guess), it’ll be in all major (evergreen) browsers.
like image 50
Emil Avatar answered Oct 16 '22 00:10

Emil


Thanks to J.Albert Bowden for the fiddle

HTML

<div id="box">
    <div class="module"></div>
    <div class="module"></div>
    <div class="module"></div>
    <div class="module"></div>
    <div class="module"></div>
</div>

CSS

#box{
    -moz-column-count:3;
    -moz-column-gap: 3%;
    -moz-column-width: 30%;
    -webkit-column-count:3;
    -webkit-column-gap: 3%;
    -webkit-column-width: 30%;
    column-count: 3;
    column-gap: 3%;
    column-width: 30%;
}
.module{
    margin-bottom: 20px;
}
like image 21
Vixed Avatar answered Oct 15 '22 23:10

Vixed


Try this.. http://jsfiddle.net/rymfev8c/1/

HTML

<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>
<div class="module"></div>

CSS

body {
  padding: 40px;
}

.module {
  height: 50px;
  width: 45%;
  margin-right: 3%;
 margin-bottom: 60px;
  background: yellow;
  float: left;
}

.module:nth-child(odd) {
  height: 100px;
  background: green;
 margin-bottom: 5px;
}
.module:nth-child(3n) {
  height: 200px;
  background: orange;
  margin-bottom: 5px;
}

omg its 3 years old..

like image 2
Shashi Avatar answered Oct 16 '22 01:10

Shashi