Infinite Scroll

Has anyone used the JS infinite scroll library?

I have an e-commerce app which needs to retrieve a list of items via ajax from my server.

I am looking for a good method to use rather than populating a list with the entire result set.

Comments ?

This was some experimental code I wrote some time ago: Basically it queries an earthquake database and builds “cards” and puts the card in the feed. Users can scroll in any direction and when they click on the card they get a new page with full details of the earthquake.

// there are two logic failures in this code:
//
// If called from onHomePageLoad() we should be forward looking - looking for new quake items.
// If we're called from the scroll monitor, we should be backward looking - looking for older items.
//
// Pass a flag to getFeed that tells us the direction: true = forward, false = backward.
//
// TEST: as new getting all items. Fixed! Works!
//       as running, scroll to bottom to get older
//       as running, check for new quake items by leaving the feed and then returning. Fixed! Works!

// JavaScript Document
var bScrollMonitor = false;
var padding = 100;    // how far from bottom before we trigger a new get
var nCards = 5;       // cards to keep in memory
var nNewest;          // first (top) card in our feed
var nOldest;          // last (bottom) card in our feed
feedQuakes = [];       // holds the quake events in the feed.

function onHomePageLoad()
{
  // initialize our feed buffer counters
  if(feedQuakes.length == 0)
  {
    // get the quake events since the last quake   
    nNewest = getLastQuake() || 0;    // last quake we have in our feed
    nOldest = 0; 
  }
  
  // if we don't have a scroll monitor, hook one up  
  $(window).scroll(function() {
    feedManager();
  });

  // get any feed updates.
  getFeed(true);
}

// return the height of the document
function getDocHeight()
{
  // per MDN: element.scrollHeight - element.scrollTop === element.clientHeight

  var D = document;
  return Math.max(
      D.body.scrollHeight, D.documentElement.scrollHeight,
      D.body.offsetHeight, D.documentElement.offsetHeight,
      D.body.clientHeight, D.documentElement.clientHeight
  );
}

function feedManager()
{
  if($(window).scrollTop() + $(window).height() >= (getDocHeight() - padding) )
  {
    getFeed(false);
  }
}

function getFeed(direction)
{

  // keep us from reentering
  if(bScrollMonitor == true)
    return;
  else
    bScrollMonitor = true;

  var url = serviceURL + 'getFeed.php';
  var last_quakeid;     // the last quake event we've viewed
  var bFlag;
  
  if(direction)
  {
    // If direction == true, get new quake items.
    last_quakeid = getLastQuake() || 0;
    bFlag = "T";
    
    // Now, if feedQuakes is empty and direction is forward
    // then we need to reset some things because the last_quakeid
    // could be the last quake available for that user and the 
    // feed would appear empty. To handle this, we'll subtract
    // 5 and get the last 5 quake events PLUS any new events.
    if(!feedQuakes.length)
    {
      last_quakeid = (last_quakeid - 5) < 0 ? 0 : (last_quakeid - 5);
      nNewest = last_quakeid;
    }    
  }
  else
  {
    // else we're looking backwards so get older items
    last_quakeid = nOldest;
    bFlag = "F";  
  } 
  
  $.ajax({
      type: 'POST',
      url: url,
      data: "userid=" +userInfo.userid +"&qid=" +last_quakeid +"&direct=" +bFlag,
      datatype: 'json',
      timeout: 5000,
      cache: false,
      beforeSend: function() {
  	     //Show spinner
         $('body').addClass('ui-loading');
   	     $.mobile.loading('show');
      }                  
  }).done(function(data, textStatus, jqXHR) {
        if(data.status == "OK")
        {
          var html;
          $.each(data.quakes, function(index, quake){
             html = buildMealCards(index, quake);
  
             // if this isn't in the array, add it!
             if(feedQuakes.length)
             {
               if(!feedQuakes.filter( obj => obj.quakeid === quake.quakeid)[0])
               {             
                 // save this quakeevent to the quakeFeed array
                 // $.extend makes a copy of the working object
                 feedQuakes.push($.extend({}, quake));
               }
             }
             else
               feedQuakes.push($.extend({}, quake));

             // if the quake event is unpublished
             // set the border to red instead of purble.

             // found that at #24 > the following if() failed
             // when it should be true. Convert to # and everything works as it should
             quake.quakeid = Number(quake.quakeid); 
             // if this quakeevent is more recent (higher quakeid)
             if( quake.quakeid > nNewest )
             {
               $("#feed").prepend(html).enhanceWithin();    // add it to the top
               setLastMeal(quake.quakeid);                      // save this as the last viewed
               // keep oldest updated too
               if(nNewest < nOldest || nOldest == 0)
                nOldest = nNewest;
               // now update newest                
               nNewest = quake.quakeid;             
             }
             else if(quake.quakeid < nOldest)     // if this is older than our current oldest (lower quakeid)
             {
               // this is an older item so add it to the bottom of the feed 
               $("#feed").append(html).enhanceWithin();
                nOldest = quake.quakeid;
             }
          });
        }
        else if(data.status == "NOFEED" || data.status == "NONEW")
        {
            // You don't have any quakes to view.
        }
        else
        {
            // We should never see this... +textStatus +" " +data.status
        }
  }).fail(function(xhr, textStatus, error) {
      ajaxError(xhr, textStatus, error, 1041);
  }).always(function(jqXHR, textStatus) {
        $('body').removeClass('ui-loading');
     	  $.mobile.loading('hide');
        bScrollMonitor = false;
  });
}

// there are two logic failures in this code:
//
// If called from onHomePageLoad() we should be forward looking - looking for new quake items.
// If we're called from the scroll monitor, we should be backward looking - looking for older items.
//
// Pass a flag to getFeed that tells us the direction: true = forward, false = backward.
//
// TEST: as new getting all items. Fixed! Works!
//       as running, scroll to bottom to get older
//       as running, check for new quake items by leaving the feed and then returning. Fixed! Works!

// JavaScript Document
var bScrollMonitor = false;
var padding = 100;    // how far from bottom before we trigger a new get
var nCards = 5;       // cards to keep in memory
var nNewest;          // first (top) card in our feed
var nOldest;          // last (bottom) card in our feed
feedQuakes = [];       // holds the quake events in the feed.

function onHomePageLoad()
{
  initMenu("homePage");
  getUserInfo();

  // initialize our feed buffer counters
  if(feedQuakes.length == 0)
  {
    // get the quake events since the last quake   
    nNewest = getLastQuake() || 0;
    nOldest = 0; 
  }
  
  // if we don't have a scroll monitor, hook one up  
  $(window).scroll(function() {
    feedManager();
  });

  // get any feed updates.
  getFeed(true);
}

// return the height of the document
function getDocHeight()
{
  // per MDN: element.scrollHeight - element.scrollTop === element.clientHeight

  var D = document;
  return Math.max(
      D.body.scrollHeight, D.documentElement.scrollHeight,
      D.body.offsetHeight, D.documentElement.offsetHeight,
      D.body.clientHeight, D.documentElement.clientHeight
  );
}

function feedManager()
{
  if($(window).scrollTop() + $(window).height() >= (getDocHeight() - padding) )
  {
    getFeed(false);
  }
}

function getFeed(direction)
{

  // keep us from reentering
  if(bScrollMonitor == true)
    return;
  else
    bScrollMonitor = true;

  var url = serviceURL + 'getFeed.php';
  var last_quakeid;     // the last quake event we've viewed
  var bFlag;
  
  if(direction)
  {
    // If direction == true, get new quake items.
    last_quakeid = getLastQuake() || 0;
    bFlag = "T";
    
    // Now, if feedQuakes is empty and direction is forward
    // then we need to reset some things because the last_quakeid
    // could be the last quake available for that user and the 
    // feed would appear empty. To handle this, we'll subtract
    // 5 and get the last 5 quake events PLUS any new events.
    if(!feedQuakes.length)
    {
      last_quakeid = (last_quakeid - 5) < 0 ? 0 : (last_quakeid - 5);
      nNewest = last_quakeid;
    }    
  }
  else
  {
    // else we're looking backwards so get older items
    last_quakeid = nOldest;
    bFlag = "F";  
  } 
  
  $.ajax({
      type: 'POST',
      url: url,
      data: "userid=" +userInfo.userid +"&qid=" +last_quakeid +"&direct=" +bFlag,
      datatype: 'json',
      timeout: 5000,
      cache: false,
      beforeSend: function() {
  	     //Show spinner
         $('body').addClass('ui-loading');
   	     $.mobile.loading('show');
      }                  
  }).done(function(data, textStatus, jqXHR) {
        if(data.status == "OK")
        {
          var html;
          $.each(data.quakes, function(index, quake){
             html = buildQuakeCards(index, quake);
  
             // if this isn't in the array, add it!
             if(feedQuakes.length)
             {
               if(!feedQuakes.filter( obj => obj.quakeid === quake.quakeid)[0])
               {             
                 // save this quakeevent to the quakeFeed array
                 // $.extend makes a copy of the working object
                 feedQuakes.push($.extend({}, quake));
               }
             }
             else
               feedQuakes.push($.extend({}, quake));

             // if the quake event is unpublished
             // set the border to red instead of purble.

             // found that at #24 > the following if() failed
             // when it should be true. Convert to # and everything works as it should
             quake.quakeid = Number(quake.quakeid); 
             // if this quakeevent is more recent (higher quakeid)
             if( quake.quakeid > nNewest )
             {
               $("#feed").prepend(html).enhanceWithin();    // add it to the top
               setLastMeal(quake.quakeid);                      // save this as the last viewed
               // keep oldest updated too
               if(nNewest < nOldest || nOldest == 0)
                nOldest = nNewest;
               // now update newest                
               nNewest = quake.quakeid;             
             }
             else if(quake.quakeid < nOldest)     // if this is older than our current oldest (lower quakeid)
             {
               // this is an older item so add it to the bottom of the feed 
               $("#feed").append(html).enhanceWithin();
                nOldest = quake.quakeid;
             }
          });
        }
        else if(data.status == "NOFEED" || data.status == "NONEW")
        {
            // You don't have any quakes to view.
        }
        else
        {
            // We should never see this... +textStatus +" " +data.status
        }
  }).fail(function(xhr, textStatus, error) {
      ajaxError(xhr, textStatus, error, 1041);
  }).always(function(jqXHR, textStatus) {
        $('body').removeClass('ui-loading');
     	  $.mobile.loading('hide');
        bScrollMonitor = false;
  });
}