I'm attempting to use jQuery Masonry in a project and it's not working properly: there's a gap in the top-right corner of the grid. I've tried adjusting the grid width and margins, which results in either one block per row or all blocks being run together (but still with a gap topright.)

It doesn't actually look like the blocks are being rearranged at all, though Masonry is applying its class and assigning absolute positioning to the elements as expected.

I was convinced that I was doing something wrong, but now I'm not so sure. I've taken a working fiddle from a similar question on Stack (http://stackoverflow.com/questions/11695574/jquery-masonry-almost-always-empty-spaces) and carefully modified it to use the dimensions I'm working with, and it just seems to be incapable of handling this selection of elements.

It works as expected.. You need to visualize the three columns you have defined.. if an element is occupying a column, then a following element can go any higher than that element on that column. (also you need to factor in your widths of the elements the gutter, so the .semi should be 2x320+30=670 the same goes for the 3 column element so 3x320+60=1020 for the .wide)
–
Gaby aka G. PetrioliSep 20 '12 at 22:32

Why can't it go any higher? Masonry's using absolute positioning. When I look at the demo pages for Masonry (and indeed, the Freetile plugin suggested by @OliverRefalo below) their examples all involve a number of disparate elements being fit together into a coherent block, with gaps only occurring when no holes are physically possible.
–
sudownedSep 20 '12 at 22:39

(And yeah, I know my widths were a little messed up - I didn't correct them because they weren't relevant, and I was concerned that my margins might have actually been contributing to the problem.)
–
sudownedSep 20 '12 at 22:40

I do not know about other plugins, but for masonry that is just how it works.. the purpose is not to make an air-tight grid. but to minimize vertical gaps. If an element blocks all columns then no following element can go above it.. See the example at the homepage of masonry and you will see gaps ...
–
Gaby aka G. PetrioliSep 20 '12 at 22:55

Looking at the Masonry title page (masonry.desandro.com), there is one gap - and it's explicitly because there's no way to arrange the pieces without a gap. In that very example, block 8 moves up a row to be above its sibling, 9.
–
sudownedSep 20 '12 at 23:10

3 Answers
3

With Masonry and Isotope for that matter, you need to remember that it all works on a module, meaning that your column widths should adhere to a least common divisor (in pixels). Then, if you have elements spanning more than one column (one module), depending on the available screen real estate (you have huge elements) the second element (much wider than first) can't be fitted to the right of the first element (much narrower than second).

Also, you're setting a fixed width to your masonry #container (#grid) which of course defies the whole purpose of the plugin.

See for yourself: remove width: 1104px; on your #grid and zoom your browser view out to the max on your fiddle page - you will see that if there's space, the second (wide black) element will eventually fit to the right of the first (narrow grey) element.

So, it all just comes down to finding a suitable least common divisor for your column width and making sure some elements aren't too large and don't span too many columns (more than two). Then it will work.

I'm open to absolutely any solution to getting a jumble of differently-sized elements to fill a space as seamlessly as possible. None of those options seem to work with my setup (or even my fiddle,) but I appreciate the effort.
–
sudownedSep 20 '12 at 23:40

As apparently this is an indelible issue with Masonry and similar solutions, I decided I'd need to roll my own here. I also decided this would be better handled in PHP, as the default, floated DIVs will have large gaps in a lot of circumstances.

Here's the algorithm I used, with comments to explain the fine points. This could have been done in jQuery trivially as well, but on the downside it'd look nasty for users without JavaScript.

$LeftPos = 0; //Tracks where we are on the grid. Our item grid is three wide, but some items may use up to three units of space.
$j = 0; //Using a second counter to track total iterations. This is to prevent infinite loops, either because of future concerns I can't predict or because of someone setting a content block to be wider than the containing grid.
for ($i = 0; $i < sizeOf($Items); $i++){
if ($LeftPos == 3){ $LeftPos = 0; } //If we filled the third column on the last iteration, we loop back round.
if ($Items[$i]['Placed'] !== true){ //If we've already put this object into the new array, skip it.
if ($Items[$i]['SpanWidth'] + $LeftPos <= 3 || $j > (sizeOf($Items) * 3)){ //If inserting this would push us past the third column, save it for when we have more room. But if we've looped over the entire array three times, chances are we're stuck for some reason so just vomit everything out so the user can look at SOMETHING, even if it's an ugly page.
$Placed++; //Increment the counter for placed objects.
$Items[$i]['Placed'] = true; //Set this item as placed, too.
$NewProducts[$i] = $Items[$i]; //Add the current item to the new array.
$LeftPos = $LeftPos+ $Items[$i]['SpanWidth']; //And calculate our new position on the grid.
}
}
if (($i+1 == sizeOf($Items) && $Placed < sizeOf($Items))) {$i = 0;} //If we reach the end and we have placed less items than we have total, loop through again.
}