Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

ryan champin
ryan champin
16,836 Points

ajax dribbble api...again

ok so i have aN 'inspiration page' thats importing pics from dribbble via ajax.....everything works fine....now im tying to add one of those add more photos infinite scroll type things. i also have managed to get this to work but bc im using masonry for my img layout.....when i click the add more button the newly added images are not being styled by the masonry JS and i cant figure out how to reload masonry to correctly position the newly ajaxed images.....heres the cooodeeeeeee....its set up to load only a few images automatically and the rest are loaded using the more button...i know theres a little bug with my count algorithm with the array but i can figure that out myself..i just need to reload masonry...i think

$(document).ready(function(){

        var shotList = [1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 688768, 1105391];
        var inc = 3;
        var loadCount = 10;

        for(var i = 0; i < 10; i++){

                var flickrAPI = "http://api.dribbble.com/shots/"+ shotList[i] +"?callback=?";
                var flickrOptions = {

                };

                function displayPhotos(data){
                var photoHTML = "";
                console.log(data);

                    photoHTML += '<div class="item">';
                    photoHTML += '<a href="' + data.image_url + '">';
                    photoHTML += '<img src="' + data.image_url + '"width="300px" height="auto"></a></div>';


                $('#container').append(photoHTML);
                }


                $.getJSON(flickrAPI, flickrOptions, displayPhotos);





        }




//this is the button that adds more ajaxed imgs     
$('#more-ajax').click(function(){

             var photoHTML = "";

            for(var x = 0; x < inc; x++){

                var flickrAPI = "http://api.dribbble.com/shots/"+ shotList[loadCount] +"?callback=?";

                var flickrOptions = {

                };

                function displayPhotos(data){

                     console.log(data);

                    photoHTML += '<div class="item">';
                    photoHTML += '<a href="' + data.image_url + '">';
                    photoHTML += '<img src="' + data.image_url + '"width="300px" height="auto"></a></div>';

                 // Make jQuery object from HTML string
                        var $moreBlocks = jQuery( data ).filter('div.block');

                        // Append new blocks
                        jQuery('#container').append( $moreBlocks );

                        // Have Masonry position new blocks
                        jQuery('#container').masonry( 'appended', $moreBlocks );

             }


                $.getJSON(flickrAPI, flickrOptions, displayPhotos); 
                loadCount++;            
            }

        });





});

3 Answers

Robert Bojor
PLUS
Robert Bojor
Courses Plus Student 29,439 Points

I have to admit that your problem was a bit challenging...

First you have to understand how Masonry works. When you launch it, it will go through the declared container element searching for the item element and pulls height and width for each item. Once the measurements are done it can rearrange the elements using poistion absolute, left and top since it knows all sizes.

When you throw images into the mix, masonry will always run before the images are loaded, hence it will bundle the items inside the container because it doesn't really know the final sizes. That's why it is recommended to use the imagesLoaded script, which pretty much, will wait until all images have finished loading and then launch masonry - And this is the case when you already have the images inside the DOM.

Now comes the plot twist... The images are dynamically added to the DOM from Dribbble and since the response from the Dribbble API can come in 100 milliseconds or 1 second, there is no certain time when you can call imagesLoaded and masonry, unless you have a fixed number of items you want to load - like in pagination, items per page, only this is items to load.

I've taken your code and moved it around a bit, well, more than a bit and I've come up with what you can see below. You can also test this online at http://www.robertbojor.com/masonry.html

You will need to get imagesLoaded ( http://imagesloaded.desandro.com/ ) and link to the correct path, I've dumped mine in the root of the site.

<html>
<head>
    <title>Masonry Test</title>
    <style type="text/css">
        .item { width:200px;}
        .item img { display:block; width: 100%; }
    </style>
</head>
<body>
<div id="container">
</div>
<br clear="all" />
<button type="button" class="moreImages">Load More</button>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://masonry.desandro.com/masonry.pkgd.min.js"></script>
<script src="imagesLoaded.min.js"></script>
<script>
var startingIndex = 0,
    loadCount = 10,   // This is how many items per load you want to get
    apiCalls = 0,     // This is a flag used to know when the last item call was made so masonry could be called
    shotList = [1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 688768, 1105391, 1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 1255447, 974182, 511102, 1284313, 688239, 1257417, 1128475, 998764, 1105391, 688768, 1105391, 998764, 1105391, 688768, 1105391],  //  A full array of items to load - This can come from a database or something
    container = document.querySelector('#container'),   // The masonry container
    msnry;           // The masonry object

function displayPhotos(data){
    apiCalls++;
    // Build the item HTML
    var photoHTML = ' \
    <div class="item"> \
        <a href="' + data.image_url + '"> \
            <img src="' + data.image_url + '"width="300px" height="auto"> \
        </a> \
    </div>';

    // Append to the big container
    $('#container').append(photoHTML);

    // Check to see if this is the last item that has to load. If true, (re)launch masonry once all the images are loaded and reset the apiCalls variable.
    if (apiCalls == loadCount) {
        imagesLoaded( container, function() {
            msnry = new Masonry( container, { itemSelector:'.item' });
        });
        apiCalls = 0;
    }
}

function loadItems(startIndex, itemLimit) {
    // Calculate pagination upper limit
    var upperLimit = itemLimit + startIndex;

    // If upper limit is more than the total items count make upper limit exactly the same as the items array length.
    if (upperLimit > shotList.length) {
        upperLimit = shotList.length;

        // Modify the maximum items per page so the apiCalls check will validate once the last item is added to the DOM.
        loadCount = upperLimit - startIndex;

        // Remove the "Load More" button so no extra calls will be made.
        $('.moreImages').remove();
    }
    for(var i = 0; i < shotList.length; i++){
        // Check to see if we're in the right section of items based on startIndex and upperLimit
        if (i >= startIndex && i < upperLimit) {
            var flickrAPI = "http://api.dribbble.com/shots/"+shotList[i]+"?callback=?";
            var flickrOptions = {};
            $.getJSON(flickrAPI, flickrOptions, displayPhotos);
        }
    }
}

$(document).ready(function(){
    // Call first batch of items (0, 10);
    loadItems(startingIndex, loadCount);
})
.on('click', '.moreImages', function(){
    // Increment starting index and call next batch of items
    startingIndex += 10;
    loadItems(startingIndex, loadCount);
})
</script>
</body>
</html>

I've peppered the code with comments but if you are not clear on how this works feel free to ask. Cheers!

Robert Bojor
PLUS
Robert Bojor
Courses Plus Student 29,439 Points

Hi Ryan,

Have you tried using the reloadItems method from masonry? ( http://masonry.desandro.com/methods.html#reloaditems ) I've had something similar built already, but I can't find it right now... I'll look in the old backups and if I find it I'll come back with some code.

ryan champin
ryan champin
16,836 Points

unless i did something wrong....i tried that and it reloaded certain images that were already loaded...not add new ones

ryan champin
ryan champin
16,836 Points

lol bro....ur great....thank you so much....i understood all of ur code except the upper limit stuff...what is that

Robert Bojor
Robert Bojor
Courses Plus Student 29,439 Points

In normal pagination controls you have a starting index and the number of results you want to get. With MySQL, for example, you have LIMIT 0,10 ( start from record 0 and return 10 ) or LIMIT 20,10 ( start from record 20 and return 10 ).

Basically the upper limit is the index limit where the function should stop adding items to the DOM. If you have an array like this: var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']; and you want to only display three items at a time the index and upper limits would be: 0-3, 3-6, 6-9, 9-11 (since we don't have a third item here).

In this case our loadCount = 3 until we reach the last block and the loadCount = 2;

Another solution would have been using the JavaScript function slice which would pretty much replace the if statement in the loop. You can read more about it here: http://www.w3schools.com/jsref/jsref_slice_array.asp