Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two methods of looping through Ajax requests for jQuery Then When - which to use?

I've got a deceptively simple blog project in the works, where I'm trying to bring together Isotope Jquery (for layout/filtering/sorting), Infinite Scroll, and dynamic loading of all blog excerpts via Ajax (so filtering and sorting is applied to all excerpts before the user scrolls down the page (after which time they're loaded into the dom and then accessible)).

This question primarily deals with getting the blog post excerpt data via Ajax, to then be passed into Isotope filtering code. I'm not sure of the best way to do this, but am currently trying to loop through each page (of blog posts excerpts) with an ajax request and then access the data as one whole.

I've come across two different methods to loop through the ajax requests, each using then when jquery statements. The first is using the method give in this SO answer, the other is simply putting the entire then when statement inside of an $.each statement.

Method 1:

  var pageCount = 15;
  var pageCountArray = [];
  for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);

  var deferreds = [];
  $(pageCountArray).each(function() {
    var pageNumber = this;
      deferreds.push(
        $.get('/page/' + pageNumber)
      )

    $.when.apply($, deferreds)
      .then(function(data){
        console.log(data); 
        // this outputs data as a string from the first page, then a list of objects
        console.log(typeof(data));
        // string
        // 13 - object
    });
  });

Slight aside: Any ideas as to why this is outputting one string and then objects?

Method 2:

  var pageCount = 15;
  var pageCountArray = [];
  for (var i = 1; i != pageCount; ++i) pageCountArray.push(i);

  $(pageCountArray).each(function(data) {
    var pageNumber = this;
      $.when(
        $.get('/page/' + pageNumber)
      ).then(function() {
        console.log(data);
        // this outputs 14 strings of data 
        console.log(typeof(data));
        // 14 - string
      })
  });

I haven't yet figured out how to incorporate the Ajaxed data into my Isotope filter function, but I think I'll need to parse this into HTML first. Still getting my footing with javascript... in this case is one of these data types (objects vs strings) easier to parse into HTML? I suppose that's the key to my answer?

Much obliged for insights.

PS: Bonus points for anyone who might know of a better way to achieve this in a different way that somehow dovetails into Isotope/Infinite Scroll nicely (perhaps in a way that's more intended to play nice with these plugins... I've been unsuccessful in my searching).

PPS: The second method feels much cleaner... anyone know of a reason that it's not a good approach (using when then inside of an .each loop)?

like image 810
Isaac Gregson Avatar asked May 29 '14 15:05

Isaac Gregson


1 Answers

Wow, this is a largely scoped question no wonder there aren't any responses. This is a massive question so I will do my very best to help. I have created many sites that include the sort/filtering of Isotope while using AJAX preload's with infinite scrolling so here is one of the simplest examples I have already written out...

First I must mention that this whole thing works much better with David DeSandro's ImagesLoaded plugin. This is mostly because it allows you to place a callback function (function to be executed once an event occurs) attached to the loading event of the final image in a given container. Wow that was wordy. How to put that better... It basically asks the container, are you done loading yet? No? How about now? You're loaded? Ok please do this function now then...

With that being implemented I would start with this code in my onLoad event like so...

$(function() {

    extendJQ_PreLoad(); //I Will Get To This Function In A Min

    //Use ImagesLoaded Plugin To Control Load Time Sync
    $(container).imagesLoaded(function() {
        cont.isotope({
            itemSelector: ".box", //This is the class I use on all my images to sort
            layoutMode: "masonry",
            isOriginLeft: true,
            isFitWidth: true,
            filter: "*",
            masonry: {
                columnWidth: ".box"
            }
        });
        preLoadNextImgSet(); //I Will Get To This Function In A Min
    });
});

Ok so let's break this down. The ImagesLoaded plugin stops the Isotope plugin instantiation from happening before there are images present to sort/filter/load and/or handle. This is step 1. Step 2 would be to then start looking at the actual isotope plugin instantiation. I am telling it to use Masonry plugin as its layout style and then I am passing in an object literal with options under the array key 'masonry'. The array key here that is named masonry is the same as any instantiation you would have normally done in the past with the stand alone Masonry plugin (non-isotope or isotope-2).

Step 3 to look at here would be my beginning call to extendJQ_PreLoad();. This function is the function I wrote to let JQuery know that I need to extend it's core functionality in order to capacitate preloading any images I give it, as an array. Like so...

function extendJQ_PreLoad() {
    $.preloadImages = function(args) {
        for (var i = 0; i < args.length; i++) {
            $("<img />").attr("src", args[i]);
        }
    }    

} //end function

This is just a simple iterator and nothing fancy, it allows the images to be preloaded by using a neat trick associated with the DOM. If you load images in this way it loads then into memory but not into the DOM meaning it is loaded and hidden. Once you then insert this image anywhere it will insert very quickly as it is now loaded in cache and awaiting placement. You can view more about this here.

Finally the last to look at is my call to my preload function. This is a very simple call to a php file that simply goes and looks for the next set of images in order, if there is any to find. If it gets some images then it begins adding it to a temporary div in memory (again not on the DOM to be seen) and is now setup for simple DOM traversal. Let's view the function to dissect its functionality...

function preLoadNextImgSet() {
    $.post('AjaxController/ajaxPreload_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
        if(data!="") {
            var y = $(document.createElement("div")).append(data).find("a"),
                found = [];
            y.each(function() {
                found[found.length] = "img/gallery/" + $(this).text();
            });
            $.preloadImages(found);
        }
    });

} //end function

In this example I have two global variables living in my browser window from JavaScript that I would have declared. A start and a loadSize variable. The start variable represents the current place in our list of images that we currently are at and the loadSize variable sets a limit on how many images to preload each time.

Now that the variables are set and sent in to the PHP file via the $.post function, we can use the PHP file to find the appropriate images in order and have them loaded into memory awaiting usage. Whatever is returned here to the y variable gets iterated over by the each function and then preloaded. Once this functions scope is exited the imaginary div will be deleted and sent to garbage as it is not used simple iterated over.

Ok, now. Its been a journey but we are almost ready to begin the final method here. Let's first go back and look at what the first imagesLoaded call was doing now that we know the new functionality added in these functions. The imagesLoaded call in the DOM-Ready event has a call in its very bottom piece that preloads the images.... why? This is because once the page loads and the initial images are loaded into the isotope container, we need the page to now use this idle time to begin already loading the next set. So in other words once the images are placed and sorted and happy to just sit there, the next loadSize amount of images will be loaded and waiting for you to place them.

Now for the final function. This function is a generic function thats sole purpose is to load in the current preloaded images into the DOM officially and then to ask for the next set to be loaded. However what on earth would be calling this function? This is where the lazyloading or infinitescroll becomes useful to us. Somewhere in your page you need to add this function in...

    $(window).scroll(function(){
        scrollTop = $(window).scrollTop(),
        windowHeight = $(window).height(),
        docuHeight = $(document).height();

        //AJAX Data Pull
        if(((scrollTop + windowHeight)+35) >= docuHeight){
            getNextImages();
        } 
    });

This function is the magic function that allows the infinitescroll effect to occur. I have added 35 pixels or so of padding (the +35 randomly in my code) because sometimes you want it to load close to the end of the page but not quite the actual end of the page.

Ok so now that this is setup when we reach the end of the page this function will want to get all of the next images generically like we had mentioned. The function of mine looks like this...

function getNextImages() {
    cont = $(container);
    $.post('AjaxController/ajaxPortfolio_Gallery.php', {currStart: start, currSize: loadSize}, function(data) {
        if(data!="") {
            //Append New Photos Inside <a> Element Tag
            var y = $(document.createElement("div")).append(data).find("a");
            cont.append(y);
            //Fix Image Layouts
            cont.imagesLoaded(function() {
                //Feed Isotope Layout The New Items
                cont.isotope("appended", y);
                cont.find("a").css({"opacity":"1"});
            });

        } else { unFilled = false; }
    });
}

I have included the unFilled variable simply so that there is a flag that can be set when you have reached the end of the images. You don't want it to keep trying to load forever if there are no images left to show.

Ok, so. This is a lot of information so I will try to keep answering as much as possible.

like image 148
GoreDefex Avatar answered Oct 11 '22 19:10

GoreDefex