Responsive Masonry layout without predefined widths
Asked Answered
B

9

7

I'm creating a 2 column masonry layout using images of different sizes. The images can be any size as long as they have the greatest common divisor (as required by the Masonry plugin).

In order to make the layout responsive I'm converting the width of the masonry items into percent (or I can use min-width and width 100%).

Update: I noticed that many who answer make both columns 50% as a solution. That works but is not the goal. Images have to retain their original image size. They can shrink but keep the same ratio.

$(function () {    
    var container = $('#container');

    // Convert .box width from pixels to percent
    $('.box').find('img').each(function () {
        var percent = ($(this).width()) / container.width() * 100 //convert to percent;
        $(this).closest('.box').css('max-width', percent + '%');
    });

    // Trigger masonry
    container.masonry({
        itemSelector: '.box',
        columnWidth: 1 //widths dividable by 1
    });
});

jsfiffle: http://jsfiddle.net/AMLqg/278/

This seems to work. The items are fluid when you resize the window. However if you load the script in a small window size (smaller than the 2 column width) the items collapse. How can I keep the masonry items responsive on window load even when the window is smaller?

Update: Here is more info for a better understanding. I'm trying to keep 2 responsive columns regardless of the window size. The columns can't have equal widths because the images have different widths. For this reason I'm using columnWidth: 1 because all widths are dividable by 1.

Please see images below for examples.

Problem: When you open the page in a small window the elements are collapsed. When you resize the window to be larger the elements remain collapsed until the window width is larger than the width of both elements.

enter image description here

Goal: I'm trying to keep the elements in 2 responsive columns on load like in the image below. Currently they remain responsive if on load the window is large and you resize it to be smaller but not vice verse when window is small on load and you make it larger.

enter image description here

Baklava answered 11/11, 2014 at 14:5 Comment(8)
Put your code inside $(window).load(function() { and try again. See if that works. Images do not have width / height, until they are loaded. $(document).ready fires, before images are loaded.Telegonus
I'm sorry, but I'm not seeing the problem in your JSFiddle. You don't want the images to stack when the window is smaller?Rainstorm
@Godisgood yes I don't want them to stack. Try making the window smaller than the container width and run the script again. You will see that they stack. I'm trying to avoid that.Baklava
You would need to make a min-width CSS property to keep that from happening. Check out @JoshBurgess's answer.Rainstorm
@Godisgood please see update. I added images to better explain the issue. Thank you!Baklava
@Telegonus $(function() has the same functionality I believe. I tried on load too but doesn't work.Baklava
If you want two responsive columns, you don't even need the masonry layout. That may be your problem, that you don't need the masonry layout.Rainstorm
I left masonry in for the edited response, but it's pointless if that's the desired output.Poteen
D
0

You can try overflow:hidden on the surrounding box.

Derril answered 11/11, 2014 at 14:40 Comment(2)
do you mean on #container? That doesn't work unfortunately.Baklava
@CyberJunkie:I had this problem, too. Maybe a bit different buy you are doing sth. wrong. Maybe you can try my solution here:#7971758. Read also the comment from the creator.BTW. did you try packery. It seems to solve many problems but its not free for commercial use.Derril
P
0

Are you looking for something like this?

Fiddle

So, all we're doing here is getting rid of your percentage calculation (of which I really don't understand the necessity), and setting a min-width on the .box class. Just like this:

.box {
    float: left;
    min-width: 100px;
}

I was able to reproduce your problem. This is how it looks for those curious:

The stacking collapse problem

The problem is your float: left rule in the CSS, which is collapsing the box when Masonry is doing its positioning calculations after adding the image. You can do a simple clear-fix to keep this if you really need to keep that clunky percentage calculation, like so:

.container:after {
   content: '';
   display: table;
   clear: both;
}

Hope that helps!

Edit – Based on your comments:

Okay, if you always want there to be two columns, it's an even simpler change:

Get rid of this Javascript

// Convert .box width from pixels to percent
$('.box').find('img').each(function () {
    var percent = $(this).width() / container.width() * 100;
    $(this).closest('.box').css('max-width', percent + '%');
});

Add this CSS

.box {
   max-width: 50%;
}

Fairly straightforward, I think.

Here's a fiddle, just for giggles

Poteen answered 13/11, 2014 at 18:46 Comment(8)
Thanks but that was not the issue. I don't want the images to collapse beneath eachother when you resize the window or load it while it's small. Basically I want to keep 2 columns regardless of the window size. I can't figure out how to do that.Baklava
Couldn't you just remove Masonry then? Seems to work for me.Poteen
I have to use masonryBaklava
I dont think elements will stack without js masonry. I also cant use html tables.Baklava
@Baklava – They'll definitely stack, and you don't need tables for this by any stretch of the imagination. Check the edit, there's very little extra you need to do given the two column constraint.Poteen
making the grid-sizer 50% adds a gap between the images when the window is larger because the images don't have equal widths. I use 1 in the columnWidth property for masonry to position the elements properly. As far as I know the layout can't be achieved with CSS alone unless both columns and images have the same widths.Baklava
And something like this doesn't solve this problem? jsfiddle.net/org6nsr8/4Poteen
Or if you wanted to force it to fill all the available space of the container: jsfiddle.net/org6nsr8/5Poteen
S
0

Using imagesloaded.js and columnwidth set using css like so:

jsFiddle

<div id="container">
<div class="grid-sizer"></div>
<div class="box">
    <img src="http://images.huffingtonpost.com/2007-11-01-ice.jpg" />
</div>
<div class="box">
    <img src="http://www.wwalls.ru/mini/201211/57608.jpg" />
</div>
<div class="box">
    <img src="http://artistsandwriters.com/site/wp-content/uploads/2014/09/IMG_7303LR-390x150-1412284267.jpg" />
</div>
</div>

Script

$(document).ready(function () {
 var container = $('#container');

 $('.box').find('img').each(function () {
     var percent = $(this).width() / container.width() * 50;
     $(this).closest('.box').css('max-width', percent + '%');
 });

 // Trigger masonry
 container.imagesLoaded(function () {
     container.masonry({
         itemSelector: '.box',
         columnWidth: '.grid-sizer'
     });
 });
 });

CSS

#container {
max-width:580px;
}
.box {
float: left;
margin-bottom: 5px;
}
.box img {
 width: 100%;
}
.grid-sizer {
 width: 50%;
 }
Souvaine answered 14/11, 2014 at 4:17 Comment(0)
S
0

EDIT

Check this http://jsfiddle.net/gk3t009j/2/

CSS

#wrapper
{
    background-color: red;
    margin: 0 auto; max-width:580px;
}

#container,
{
       overflow: hidden; 
       width: 100%;
}
.box
{       
    max-width: 290px!important; width: 50%;     
}

.box img{width: 100%;}

JS

$( window ).load( function()
{
    var wc=$( '#container').width();
    wc=parseInt(wc);
    if( wc % 2) 
    {
        var wb=$('.box').width();
        wb--;
        $('.box').width(wb)
    } 


    $( '#container').masonry(
    {
        itemSelector: '.box',
        columnWidth: function( containerWidth ) {
            return parseInt(containerWidth / 2);
          }

    });
});

HTML

<div id="wrapper">
    <div id="container">
        <div class="box">
            <img src="http://images.huffingtonpost.com/2007-11-01-ice.jpg" />
        </div>



        <div class="box">
                <img src="http://www.wwalls.ru/mini/201211/57608.jpg" />
            </div>
            <div class="box">
                <img src="http://artistsandwriters.com/site/wp-content/uploads/2014/09/IMG_7303LR-390x150-1412284267.jpg" />
            </div>
    </div>
</div>
Stiver answered 14/11, 2014 at 10:36 Comment(2)
Thanks but I cannot use that code because you are changing teh original width of the images.Baklava
I changed the code above. When the width of the container is odd, I noticed that masonry make one column.Stiver
F
0

I removed the JS code and some of the HTML markup and updated the styling:

#container {
    width: 100%;
}
img {
    display: inline;
    vertical-align: top;
    float: left;
    min-width: 50%;
    width: 50%;
}

http://jsfiddle.net/org6nsr8/8/ I agree with Josh Burgess on that Masonry is not needed to accomplish this, take a look and see if this is what you're after.

I'd be happy to elaborate if something is unclear or you want anything explained.

Feudalize answered 18/11, 2014 at 9:21 Comment(1)
Thanks for the help. I can't use 50% for each column and alter the original image size. The goal is to use images of different widths.Baklava
S
0

You don't need the JavaScript; just change the css for .box to:

.box {
    float: left;
    max-width: 50%;
}
Selfseeking answered 19/11, 2014 at 7:32 Comment(0)
I
0

I am not sure if this is what you need. If I understood the problem correctly may be you need to use max-width instead of width.

Here is example fiddle : http://jsfiddle.net/AMLqg/304/

My JS code :

        $(function () {

            var container = $('#container');
            var maxWidth = container.css("maxWidth");
            maxWidth = parseInt(maxWidth.substring(0,maxWidth.indexOf("px")));
            // Convert .box width from pixels to percent
            $('.box').find('img').each(function () {
                var percent = ($(this).width()) / maxWidth * 100;
                console.log(percent);
                $(this).closest('.box').css('max-width', percent + '%');
            });

            // Trigger masonry
            container.masonry({
                itemSelector: '.box',
                columnWidth: 1 //widths dividable by 1
            });
        });
Idiopathy answered 19/11, 2014 at 10:10 Comment(0)
E
0

After trying several library to make a masonry layout , I prefer salvattor.js

Very easy to use. the size of the columns you can configure css.

@media screen and (max-width: 480px){
    #grid[data-columns]::before {
        content: '1 .column.size-1of1';
    }
}
Eris answered 19/11, 2014 at 15:18 Comment(0)
B
0

What i understand you want to keep Layout 2 Column with Images on aspect ratio on all screen sizes ,

Check

http://jsfiddle.net/tasaeed/k40cgfye/

CSS

#container {
  max-width: 580px;
}

.box {
  float: left;
  width:50%;
}

.box img {
  width: 100%;
  height:auto;
}

Script

$(function () {

    var container = $('#container');

    // Trigger masonry
    container.masonry({
        itemSelector: '.box',
    });
});
Bluefield answered 20/11, 2014 at 7:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.