13

I am setting up a site for a photographer. It should be built using the Bootstrap 3 framework, and he wants to have a masonry with over 400 images on one page. For this to work LazyLoad is a must. I have now spent several days trying to get LazyLoad to work with Desandros Masonry but with no success..

I've tried all of the examples that one finds googling, but most posts/sites/forums just redirects you, or have copied this stackoverflow answer: Combining LazyLoad and Jquery Masonry

I've tried both methods but unfortunately I get nothing but grey hair..... :(

Here is a simplified live version of the page im working on:
http://nr.x10.mx
In this example I have added a fade-in on page-load, but left the LazyLoad out since I can get it to work.

And here you have a FIDDLE of the following

var container = document.querySelector('#ms-container');
imagesLoaded( container, function() 
{
   var msnry = new Masonry(container, 
                                     { itemSelector: '.ms-item',
                                       columnWidth: '.ms-item',});
}); 

You can also download the whole pack here, including the jquery.lazyload.js HERE

Any help would be highly appreciated


UPDATE

Here you can have 4 different examples of the different problems that occur. I also found to my joy that the Bootstrap .img-responsive class is interfering with LazyLoad.

1 - Masonry without LazyLoad
2 - Masonry and Lazyload - Masonry breaks down and LazyLoad has no effect
3 - LazyLoad without Masonry - LazyLoad has no effect
4 - LazyLoad without Masonry and Bootsrap "img-responsive" removed
5 - Masonry & LazyLoad using first method of SO answer mentioned above
6 - Masonry & LazyLoad using second method of SO answer mentioned above
Both of the last ones gives the following error: [Error] TypeError: 'undefined' is not a function (evaluating '$container.imagesLoaded') global code (5.html, line 110)

Updated zip HERE

Again, any asisstance would be highly appreciated, thank you

Community
  • 1
  • 1
no0ne
  • 2,659
  • 8
  • 28
  • 44

4 Answers4

7

I made your 5.html work by using the javascript files of the fiddle present on the SO link you posted, answered by Nathan Do. So you probably just had bad versions of the scripts.

The scripts linked on that page are: http://cdn.jsdelivr.net/masonry/2.1.08/jquery.masonry.min.js and http://cdn.jsdelivr.net/jquery.lazyload/1.8.4/jquery.lazyload.js

Here's your original fiddle updated with that page's method: http://jsfiddle.net/L9RLe/

Community
  • 1
  • 1
ariel
  • 15,620
  • 12
  • 61
  • 73
  • Looking good Ariel. Except that when I add the Bootstrap CSS file the Masonry layout gets thrown off and the '.item' elements (images) loads overlaping each other. Your update also seems to have knocked out the '.image-resize' - perhaps the solution would be to ditch Bootstrap altogether... – no0ne Feb 04 '14 at 00:38
  • Your fiddle looks ok here. What you need bootstrap for ? – ariel Feb 04 '14 at 02:21
  • Bootstrap is part of my client's demands. In the fiddle I get the images loaded on top of each other - you don't? strange.. – no0ne Feb 04 '14 at 02:41
  • You are right, works fine on Chrome and FF but buggy on Safari. – no0ne Feb 04 '14 at 06:20
  • so now all I need is to fixt that and make it work with Bootstrap – no0ne Feb 04 '14 at 06:37
  • Hi, are you still having problems? Please let me know if you can find a code that can show the problem on chrome/ff or even safari (which i didn't had problems as well) and we figure it out. – ariel Feb 05 '14 at 22:53
  • same goes for you ariel, did you manage to get this to run with Bootstrap also? – no0ne Feb 06 '14 at 23:36
  • i really don't know what you talking about. here your fiddle with the bootstrap files look 100% fine. i don't know what you mean by .image-resize or .image-responsive. can you explain better? – ariel Feb 07 '14 at 00:40
  • can we take this to a private chat? There used to be a Chat button but now I cant find it.. – no0ne Feb 07 '14 at 02:57
  • please expand your question – ariel Feb 07 '14 at 03:00
  • what i need is for the images to adapt to the div size e.i. '.image-responsive' class of Bootstrap. Compare http://nr.x10.mx with this http://jsfiddle.net/L9RLe/1/ resize the browser window and you'll see a huge difference. – no0ne Feb 07 '14 at 03:08
  • In the fiddle the only element that is responsive are the columns not the content inside – no0ne Feb 07 '14 at 03:10
  • that's all? just replace `width: 200px` with `width: 24%` – ariel Feb 07 '14 at 03:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46980/discussion-between-no0ne-and-ariel) – no0ne Feb 07 '14 at 03:13
  • >> End comment << this answer was the clearest and most helpful. But I am not confident with this since I still get image overlapping on Safari (tested on two different computers using Safari 7.0 OSX 10.9 and Safari 6. on OSX 10.7) - Thank you Ariel!! – no0ne Feb 07 '14 at 23:07
3

In your case, though you are creating masonry and adding lazyload effect, the images get overlapped.

You need to follow steps as :

  1. Create Dynamic HTML structure for images along with their respective aspect ratio height and width. Note: images should have the required attributes for applying lazy load effect i.e. class="lazy" data-original="actual image url" and src="dummy imageurl".

  2. Apply lazy load effect.

  3. Then create Masonry.

Lets have an example :

Suppose I am having a javascript array with some image related data as,

var gallery_itemList= [
    {url: "images/example1.jpg", width:"1170", height:"460"},
    {url: "images/example2.jpg", width:"800", height:"320"},
    {url: "images/example3.jpg", width:"1200", height:"870"}];

And below prototype for creating dynamic html, applying lazyload effect and creating Masonry effect as :

var masonryGallery = {
    gallery :'', // gallery to create
    genarateGallery : function() {
      // generate each item html
      var inHTML="", i;
      for(i=0;i<gallery_itemList.length;i++){
        var iWidth, iHeight, fHeight=0;
        iWidth=parseInt(gallery_itemList[i].width);
        iHeight=parseInt(gallery_itemList[i].height);
        fHeight = Math.round((iHeight*300)/iWidth);

        inHTML+='<div class="item" style="height:'+fHeight+'px">';
        inHTML+='<img class="lazy" src="images/loading.gif" data-original="'+gallery_itemList[i].url+'"/>';
        inHTML+='</div>';   
      }
      //add generated html to gallery
      $(masonryGallery.gallery).append(inHTML);       
    },
    applyLazyload : function(){
      $("img.lazy").lazyload();
    },
    createMasonry : function(){
      // create Masonry
      $(masonryGallery.gallery).masonry({
        columnWidth: 350,
        itemSelector: '.item',
        isFitWidth: true,
        isAnimated: !Modernizr.csstransitions
      }).imagesLoaded(function() {
        $(this).masonry('reload');
      });
    },
    init : function(givenGallery) {
        masonryGallery.gallery = givenGallery; // set gallery 
        masonryGallery.genarateGallery(); // generate gallery html
        masonryGallery.applyLazyload();  // apply lazyload effect
        masonryGallery.createMasonry();  // apply masonry effect
    }
};

/* Gallery Intialisation */
(function(){masonryGallery.init('div#content');})(); 
Mazzu
  • 2,799
  • 20
  • 30
  • Hi Mazzu, this looks interesting. Any chance of having a FIDDLE of this? – no0ne Feb 05 '14 at 03:16
  • Hey noOne, your fiddle http://jsfiddle.net/L9RLe/ works fine, you may integrate above code into it. :) – Mazzu Feb 05 '14 at 12:18
  • 1
    I was not able to integrate your code.. Will this work with Bootstrap? If so, you are one fiddle away from a 100 juicy points ;) – no0ne Feb 06 '14 at 23:16
1

If you have the problem images get overlapped, I found the solution at the site below, although it is in Japanese.

http://www.webdesignleaves.com/wp/jquery/1340/

The point is use following;

$('img.lazy').load(function(){ ... })

HTML

<div id="works_list">
<div class="work_item">
    <img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/001.jpg" alt="">
    <p>title 1</p>  
</div><!-- end of .work_item-->
<div class="work_item">
    <img class="lazy" src="images/dummy.gif" data-original="images/works/thumb/002.jpg" alt="">
    <p>title 2</p>  
</div><!-- end of .work_item-->
     ....
</div><!-- end of #works_list -->    

jQuery

$("img.lazy").lazyload({
    effect: 'fadeIn',
    effectspeed: 1000,
    threshold: 200
});

$('img.lazy').load(function() {
    masonry_update();
});

function masonry_update() {     
    var $works_list = $('#works_list');
    $works_list.imagesLoaded(function(){
        $works_list.masonry({
            itemSelector: '.work_item', 
            isFitWidth: true, 
            columnWidth: 160
        });
    });
 }
softk5
  • 101
  • 1
  • 2
1

Just want to contribute my solution to this thorny problem.

Basically you need to call masonry's layout() function every time an image is lazyloaded, because that's when you know what it's dimensions are going to be. So you setup lazyload's load() function to call masonry.layout(). This can cause a new problem, because on your initial page load, all of your images will have a zero or near-zero height (depending on your css), and thus stack up in the top of the viewport. When you initialize lazyload, it will see all these images in the viewport, and try to load them all at once. This causes you to download tons of images, and even worse, calls masonry dozens or hundreds of times. Not very speedy.

My solution is to force a minimum height on unloaded images until they are lazyloaded. This restricts the number of images that will be found in the viewport initially, limiting the number of lazyload and masonry calls to a reasonable amount.

Once an image is loaded, you remove the .unloaded class in order to deconstrict the height and allow it to conform to the height of the image.

HTML

<div id="masonry-container">
    <div class="masonry-item unloaded"><img class="lazy" data-original="image.jpg"></div>
    <!-- Repeat as needed -->
</div>

CSS

.masonry-item {
    width: 33.3%; // I'm doing a 3-column masonry layout
}

.unloaded {
    min-height: 200px;
    // set a minimum default height for image containers
    // this prevents too many images from appearing in the viewport
    // thus causing lazy load to load too many images at once
    // we will use the lazyload .load() callback to remove this fixed 
}

JS

$( document ).ready(function() {    
    // Initialize Masonry
    var container = document.querySelector('#masonry-container');
    var msnry = new Masonry( container, { transitionDuration: 0 } );
    msnry.layout(); // run masonry for first time

    function doMasonry() { msnry.layout(); } // gives us a function handler to call later with window.setTimeout()

    // Initialize lazyload
    $("img.lazy").lazyload({
        effect : "fadeIn", // optional
        load : function() {
            $(this).parent().removeClass("unloaded"); // select the div containing the image and remove the "unloaded" class, thus deconstricting the min-height
            window.setTimeout(doMasonry,100); // trigger masonry again with a 100 ms delay, if we do it too soon, the image may not have loaded, and masonry will layout with the wrong image dimensions (this might be a bit of a hacky way to do it)
        }
    });
});
Community
  • 1
  • 1
beibei2
  • 795
  • 1
  • 7
  • 13