…or Pinterest-style layout. jQuery Masonry is a jQuery-based plugin for a dynamic grid layout built by David DeSandro. What it does is “arranges elements vertically, positioning each element in the next open spot in the grid” and what you get is “minimized vertical gaps between elements of varying height”. Sounds too higgledy-piggledy? Remember the social network you quit a while ago because of the obvious reasons, whereas your wife/girlfriend still uses it on a daily basis and you can't get her off from this? Bull's eye, that's Pinterest. So this time, as said before, Pinterest-style layout made responsive.
Sheepy Me is a website where I constantly combine time-tested techniques and new trends. As for the blog where pictures act big in both ways, Pinterest inspired layout (a trend, popularized by Pinterest) probably was the best choice for listing items attractively. Although, JavaScript-based layout is not a good practice and should be avoided while CSS is capable to do this instead. This time, so far, it's not. Will take a look closer at this later in the post anyway.
jQuery Masonry is not the only plugin and method for achieving the same effect, but it was the one actually working as well as working in the way I prefer. In responsive way. We're at the nice times when not thinking responsively is a bad habit and non-responsive techniques are considered as, to put it mildly, poor ones. jQuery Masonry is responsive-friendly, but that's not enough and some extra work is necessary in order to make it all meaningful.
The Grid
Three-column grid is when screen width is higher than 640 pixels. Two columns – when screen width is equal to or less than 640 pixels, but is more than 320 pixels. And one column – when screen width is equal to or less than 320 pixels. The scheme may be easily adjusted to any other manner.
Setting It Up
Apart from the obvious include-jQuery-first, let's see how actual things look like before bringing them to the world of responsiveness.
HTML structure is the simplest thing here. The further content goes into .item elements. Thinking of better semantics, it would be nicer to go with unordered list. At Sheepy Me I used <ul>'s for comments, date and tags to present into .item's. To avoid any conflict and keep CSS clean, div's was the only choice.
<div id="wrapper"> <div id="list"> <div class="item"> ... </div> <div class="item"> ... </div> <div class="item"> ... </div> <!-- ... --> </div> </div>
CSS is a little bit more specific as it puts our layout to fluid mode. Instead of ritual px, I use em (based on browser's default size 16px, 100%) and % to define sizes and so to achieve complete flexibility across the browsers, screen sizes and people habits.
#wrapper
{
max-width: 60em; /* 960 px */
margin: 0 auto;
}
#list
{
width: 103.125%; /* 990px */
overflow: hidden;
margin-left: -1.562%; /* 15px */
margin-bottom: -1.875em; /* 30px */
}
.item
{
width: 30.303%; /* 300px */
float: left;
margin: 0 1.515% 1.875em; /* 15px 30px */
}
What's interesting, I did a small workaround to solve the problem of horizontal margins by setting the width of #list to more than 100% and left margin to the negative value. Though jQuery Masonry manages horizontal spacing automatically, the workaround gracefully degrades when JavaScript is disabled in the browser.
Finally some JavaScript. Only shameless initialization of the plugin for now.
$( window ).load( function()
{
$( '#list' ).masonry( { itemSelector: '.item' } );
});
Pouring In Responsiveness
Now that we have a working formation, let's teach it to adapt to savage environments: small and constantly changing screen sizes as well as zooming without remorse. However, the zoom is not handled well enough due to the absence of bulletproof active zooming detection.
HTML stays the same, but CSS code gets a makeup – media queries. The first of them transforms the grid to two-column one, whilst the second to the simple one-column layout.
#wrapper
{
max-width: 60em; /* 960 px */
margin: 0 auto;
}
#list
{
width: 103.125%; /* 990px */
overflow: hidden;
margin-left: -1.562%; /* 15px */
margin-bottom: -1.875em; /* 30px */
}
.item
{
width: 30.303%; /* 300px */
float: left;
margin: 0 1.515% 1.875em; /* 15px 30px */
}
@media only screen and ( max-width: 40em ) /* 640px */
{
.item
{
width: 46.876%; /* 305px */
margin-bottom: 0.938em; /* 15px */
}
}
@media only screen and ( max-width: 20em ) /* 640px */
{
#list
{
width: 100%;
margin-left: 0;
}
.item
{
width: 100%;
margin-left: 0;
margin-right: 0;
}
}
What's for JavaScript, function setColumns(); initially counts the number of columns accordingly to the screen width. $( window ).resize( setColumns ); does the same, but when window is resized. jQuery Masonry, luckily, has an option named columnWidth. Putting under (for value) a function actually solves the problem.
$( window ).load( function()
{
var columns = 3,
setColumns = function() { columns = $( window ).width() > 640 ? 3 : $( window ).width() > 320 ? 2 : 1; };
setColumns();
$( window ).resize( setColumns );
$( '#list' ).masonry(
{
itemSelector: '.item',
columnWidth: function( containerWidth ) { return containerWidth / columns; }
});
});
That's it! Sheepy Me is a real life example of the whole experience. Besides that, I made a separate demo which consists only of the relevant code and helps to focus on the technique itself.
CSS-only solution?
Kushagra Agarwal suggests to do without JavaScript and use CSS's column-* properties instead. So how CSS code would change? #wrapper remains the same, but the rest:
#list
{
width: 100%;
overflow: hidden;
margin-bottom: -1.875em; /* 30px */
-webkit-column-count: 3;
-webkit-column-gap: 1.875em; /* 30px */
-webkit-column-fill: auto;
-moz-column-count: 3;
-moz-column-gap: 1.875em; /* 30px */
-moz-column-fill: auto;
column-count: 3;
column-gap: 1.875em; /* 30px */
column-fill: auto;
}
.item
{
margin-bottom: 1.875em; /* 15px 30px */
-webkit-column-break-inside: avoid;
-moz-column-break-inside: avoid;
column-break-inside: avoid;
}
@media only screen and ( max-width: 40em ) /* 640px */
{
#list
{
-webkit-column-count: 2;
-webkit-column-gap: 0.938em; /* 15px */
-moz-column-count: 2;
-moz-column-gap: 0.938em; /* 15px */
column-count: 2;
column-gap: 0.938em; /* 15px */
}
.item
{
margin-bottom: 0.938em; /* 15px */
}
}
@media only screen and ( max-width: 20em ) /* 320px */
{
#list
{
-webkit-column-count: auto;
-moz-column-count: auto;
column-count: auto;
}
}
How lovely is that, even with those vendor prefixes. It would absolutely be the best technique, if it hadn't the following limitations:
- Not supported by IE9 and below;
- Different rendering among supported browsers. Safari and Chrome does in one way, whilst Firefox and Opera in another. And both manners doesn't seem to be logical enough.
Conclusion
column-* isn't fully standardized yet across web browsers. jQuery Masonry is a JavaScript thing. Two evils. You can stay with just one of them. You can combine them, by attaching jQuery Masonry only to IE9-and-below or JavaScript-less users and applying CSS columns for the rest. Whichever your decision is, just make sure it's best for your users.



40 comments. Write?
It’s looking good but i’ll stick with Twitter Bootstrap. Great post by the way!
Just did that. In 40 lines of code. Animated. Using dojo+bootstrap.
We could easily make a module from that.
Tell me if you need a demo. I do follow-up here.
Go ahead, let us know what have you came up with :)
OK,
after 10 years I am redesigning my own site, that is what I did it for.
It is just a concept for now with blind text and -images.
demo is NOT crossbrowser tested so far!
However -
The code is in the .js onresize event now, but it could be a standalone module off course.
demo:
http://the-a-team.info/deliver/demoBootstrapMasonry/#!/pages/stories
javascript - LINES 465-515 :
http://the-a-team.info/deliver/demoBootstrapMasonry/assets/framework/js/application.js
Wow, what a backhanded compliment Romain… ;-)
Great post—love the example, nice work. Thanks for sharing this technique—simple and elegant.
Lol yeah i just read again my comment and it’s a bit harsh. Don’t get me wrong, i wanted to say your post is very clear, very interesting to read :)
Good news is that IE10 will support “column-count”. As browsers evolve to support (future) CSS standards, we can drop javascript polyfills one by one. #Joy!
CSS works better…. brilliant work, anyway
Great article Osvaldas! I see some comments mentioning frameworks, but I (personally) am more interested in the _techniques_ used to create the responsive page, versus the use of a responsive framwork.
I didn’t think you need masonry at all with just a little CSS. Especially since the columns are fixed width. Float: left; clear: both; Right?
Nothing is ‘fixed’ what is responsive. Moreover, all of the widths and heights are totally dynamic here :)
Very nice write up - thanks for the inspiration
This is so awesome! I have been playing around with this layout for the last few days. Tried my hands at jQuery Masonry, Isotope, what not. Finally, settled with CSS3 multi columns to increase the performance and faster loading. Though not supported on all the browsers, so will either use masonry or isotope as a fallback…
Thank you for writing this ^_^
Great standpoint. Thanks for a comment.
Can Share that code here
Using The CSS3 and fallback masonary code
Would be great if you could share the code! Thanks.
Hey everyone,
Sorry for the late reply. Landed on this article after a very long time :|
Anyway, I wrote a short tutorial on how to use CSS3 multi columns to create a masonry. Also it has jQuery masonry as a fall back, if CSS3 multi columns are not supported. Let me know your thoughts.
Thank you :)
ooops, sorry! Forgot to add the link.
http://www.code-pal.com/css3-based-masonry-layout-with-jquery-masonry-as-fallback/
@Admin: Can you edit the comments and merge them please.
Thank you.
Awesome!!!
CSS Only Solutuion small error on line 3 at width: 100%: (colon)
This should come of help to me in my next project as the requirements sit exactly as the result of this tutorial. Romain, yours is an interesting solution too.
P.S - ‘em’ rocks :)
Puikus straipsnis! :)
The main problem of CSS columns variant is wrong posts order. Latest post should be in top row, not in left column. Recently I made a website with similar layout, but I decided not to use Masonry because it’s quite slow. And I also wanted to fill whole page width with images without any gaps between them. Thats why I wrote my own implementation. You can see how it works here: breslavtsev.com/new/ (sorry, it’s unfinished site, don’t want it to be indexed) and source are here.
Any example of its usage?
Yep, there was an URL in my comment: breslavtsev.com/new/ — just change window width to see how layout changes.
your mosaicflow.js is good! It’s so smooth, not slow and simple editing for css. Thanks!
After having seen numerous sites with masonry-like galleries I Google it and stumbled upon… Masonry.js no less (no pun intended). I’ve been wowed over by the look and I intend to use it with some finesse when I develop my own portfolio (in process, actually). I’m going to use the css only approach for all modern (sans IE) browsers and Masonry.js for IE.
Interesting CSS solution.
I did something similar a couple weeks ago, only using :nth selectors, you can check it out here in the videos part:
http://clients.robertoalanis.com/c2013/
I am looking for a setup that handles videos like yours. can you explain how you setup the code on the site?
You have done really GOOD Job.
Thanks
Has anyone used this on OS Class?
Thank you for this fantastic tutorial. I notice on the demo that you prepared, there is the usual overlapping of the blocks that occurs when you resize the window, whereas the overlapping seems much less noticeable on the Sheepy Me website. I just wondered if there was something extra you’ve done to compensate for this on the Sheepy Me site?
To elaborate a little, please visit the question I have opened on Stack Overflow. I used screenshots from your nice demo page – hope you don’t mind! http://stackoverflow.com/q/15554798/2110499
Hi Sam. Thanks. I am using this on Sheepy Me:
$( window ).resize( function(){ $( ‘#list’ ).masonry( ‘reload’ ); });Hi Osvaldas. Many thanks for your fast reply. What a fantastic solution! It works perfectly.
I’m actually using Isotope for this, so I had to find a slightly different method, but it seems to do the same:
$(window).resize( function(){ $(’#list’).isotope(‘reLayout’); });Thanks again.
good work and good examples…
Great work, great examples, and great comments to read through! I have a new project where I will be implementing this. Thanks, guys!
DUDE! Not seeming to get this to work and it is driving me Ca-Razy!
My goal is to meet the following criteria:
1. No set heights - no set widths
2. Always a good amount of columns
3. Centered
4. Responsive
Now so far I think I have 1-3 .. but 3 is stopping 4.. Basically the “container” of masonry is centered but now it is not responsive unless you reload the page. I tried : “$( window ).resize( function(){ $( ‘#list’ ).masonry( ‘reload’ ); });” but it didn’t do ANYTHING… So I am wondering.. how would I get masonry to actively reload the information on browser resize. I am super confused.
Hi Ted, I’ve just been suffering with your problem and the “$( window ).resize( function(){ $( ‘#list’ ).masonry( ‘reload’ ); });” seemed to work for me. However, all I did was change the existing code from: “( window ).load( function()
{ $( ‘#list’ ).masonry( { itemSelector: ‘.item’ } );
});”
to: “( window ).resize( function()
{ $( ‘#list’ ).masonry( { itemSelector: ‘.item’ } );
});”
Hope this helps! :)
Thanks so much! This works brilliantly on my blog: http://www.Davies-Design.com/blog
It was easy to set up and very simple. Much appreciated!
Mike.
This is slick as balls!