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 trialryan champin
16,836 Pointsajax 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
Courses Plus Student 29,439 PointsI 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
Courses Plus Student 29,439 PointsHi 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
16,836 Pointsunless i did something wrong....i tried that and it reloaded certain images that were already loaded...not add new ones
ryan champin
16,836 Pointslol bro....ur great....thank you so much....i understood all of ur code except the upper limit stuff...what is that
Robert Bojor
Courses Plus Student 29,439 PointsIn 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