5

QUESTION:

How to implement efficient infinite scrolling in Firebase using javascript (and node.js) ?


WHAT I CHECKED:

Implementing Infinite Scrolling with Firebase?

Problem: older firebase ^

Infinite scroll with AngularJs and Firebase


CODE FROM: Infinite scroll with AngularJs and Firebase

"First, I recommend to create an Index in your Firebase. For this answer, I create this one:

{
   "rules": {
      ".read": true,
      ".write": false,
      "messages": {
         ".indexOn": "id"
      }
   }
}

Then, let's make some magic with Firebase:

// @fb: your Firebase.
// @data: messages, users, products... the dataset you want to do something with.
// @_start: min ID where you want to start fetching your data.
// @_end: max ID where you want to start fetching your data.
// @_n: Step size. In other words, how much data you want to fetch from Firebase.
var fb = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/');
var data = [];
var _start = 0;
var _end = 9;
var _n = 10;
var getDataset = function() {
   fb.orderByChild('id').startAt(_start).endAt(_end).limitToLast(_n).on("child_added", function(dataSnapshot) {
      data.push(dataSnapshot.val());
   });
   _start = _start + _n;
   _end = _end + _n;
}

Finally, a better Infinite Scrolling (without jQuery):

window.addEventListener('scroll', function() {
  if (window.scrollY === document.body.scrollHeight - window.innerHeight) {
     getDataset();
  } 
});

I'm using this approach with React and it's blazing fast no matter how big your data is."

(answered Oct 26 '15 at 15:02)

(by Jobsamuel)


PROBLEM

In that solution, n posts will be loaded each time the scroll reaches the end of the height of screen.

Depending on screen sizes, this means a lot more posts than needed will be loaded at some point (the screen height only contains 2 posts, which means 3 more posts than necessary will be loaded each time we reach the end of the screen height when n = 5 for example).

Which means 3*NumberOfTimesScrollHeightHasBeenPassed more posts than needed will be loaded each time we reach the end of the scrollheight.


MY CURRENT CODE (loads all posts at once, no infinite scrolling):

var express = require("express");
var router = express.Router();

var firebase = require("firebase");

router.get('/index', function(req, res, next) {

    var pageRef = firebase.database().ref("posts/page");

    pageRef.once('value', function(snapshot){
        var page = [];
        global.page_name = "page";
        snapshot.forEach(function(childSnapshot){
            var key = childSnapshot.key;
            var childData = childSnapshot.val();
            page.push({
                id: key,
                title: childData.title,
                image: childData.image
            });
        });
        res.render('page/index',{page: page});
    });
});
Community
  • 1
  • 1
Coder1000
  • 4,071
  • 9
  • 35
  • 84

1 Answers1

3

Here is full code for infinite paging.

The function createPromiseCallback is for supporting both promises and callbacks.

function createPromiseCallback() {
  var cb;
  var promise = new Promise(function (resolve, reject) {
      cb = function (err, data) {
          if (err) return reject(err);
          return resolve(data);
      };
  });
  cb.promise = promise;
  return cb;
}

The function getPaginatedFeed implements actual paging

function getPaginatedFeed(endpoint, pageSize, earliestEntryId, cb) {
  cb = cb || createPromiseCallback();

 var ref = database.ref(endpoint);
  if (earliestEntryId) {
    ref = ref.orderByKey().endAt(earliestEntryId);
  }
  ref.limitToLast(pageSize + 1).once('value').then(data => {
    var entries = data.val() || {};

    var nextPage = null;
    const entryIds = Object.keys(entries);
    if (entryIds.length > pageSize) {
        delete entries[entryIds[0]];
        const nextPageStartingId = entryIds.shift();
        nextPage = function (cb) {
            return getPaginatedFeed(endpoint, pageSize, nextPageStartingId,                     cb);
        };
    }
    var res = { entries: entries, nextPage: nextPage };
    cb(null, res);
  });
  return cb.promise;
}

And here is how to use getPaginatedFeed function

var endpoint = '/posts';
var pageSize = 2;
var nextPage = null;
var dataChunk = null;
getPaginatedFeed(endpoint, pageSize).then(function (data) {
    dataChunk = data.entries;
    nextPage = data.nextPage;

    //if nexPage is null means there are no more pages left
    if (!nextPage) return;

    //Getting second page
    nextPage().then(function (secondpageData) {
        dataChunk = data.entries;
        nextPage = data.nextPage;
        //Getting third page
        if (!nextPage) return;

        nextPage().then(function (secondpageData) {
            dataChunk = data.entries;
            nextPage = data.nextPage;
            //You can call as long as your nextPage is not null, which means as long as you have data left
        });
    });

});

What about the question how many items to put on the screen, you can give a solution like this, for each post give fixed x height and if it requires more space put "read more" link on the bottom of the post which will reveal missing part when user clicks. In that case you can keep fixed count of items on your screen. Additionally you can check screen resolution in order to put more or less items.

Vladimir Gabrielyan
  • 801
  • 1
  • 11
  • 23
  • When and how should the feed be triggered ? I would like to make sure the website loads only as many posts as the screen height can fit and loads more only when we scroll 1*scrollHeight. – Coder1000 Oct 24 '16 at 17:08
  • 1
    Initially you have to decide how many posts you want to show on your screen and intialize the function `getPaginatedFeed(endpoint, pageSize)` with correct `pageSize`, and then every time your scroll event trigerrs you can call `nextPage` function which will give you another chunk of data. Your scroll event triggers only when you reach to the bottom of your data. – Vladimir Gabrielyan Oct 24 '16 at 17:13
  • But what should my scroll event be ? If I do scrollheight, the result will stay the same: 2 posts, whatever their size may be, will be loaded onto the screen. If the 2 posts have a combined height higher than my screen, then I will be loading more data than necessary and therefore wasting bandwidth. If the 2 posts combined height is much smaller than my screen size (If I have a very big screen), then the user will have to scroll much more than necessary and there will be big empty spaces between every 2 posts. That's the problem :/ – Coder1000 Oct 24 '16 at 17:16
  • Wait, did you just say my scroll event triggers only when I reach the end of my data and not the end of my screen ? Oooppss. You either edited your comment in-between, or I totally misread it :) That settles it then ! I will try your code in the next 24hours and get back to you if I have any issues implementing it ^^ – Coder1000 Oct 24 '16 at 17:19
  • In the mean time, would you be so kind to check my other question ? http://stackoverflow.com/questions/40223230/how-to-trigger-a-server-request-when-button-is-clicked-node-js – Coder1000 Oct 24 '16 at 17:22
  • 2
    Okay I see your problem, my code is purely backend implementation of infinite scrolling, what you are asking is more frontend problem. So I have a couple of options for that. 1. You can show all your posts with fixed X height and if it requires more space you can put one little button or link which will say "read more" as are doing all social sites. 2. The problem with different size of screens you can solve by detecting initially the height of screen and then depending on that decide how many posts you want to put(remember each post should have X height) – Vladimir Gabrielyan Oct 24 '16 at 17:24
  • Could you update your answer with that code please, so I can accept it ? (but keep your old code also) – Coder1000 Oct 24 '16 at 17:55
  • @Coder1000 it highly depends of your frontend framework, it can be ios, android, web, etc.. So important thing is to understand approach the other details you can brainstorm yourself, I don't think that stackoverflow is the place to implement everything. – Vladimir Gabrielyan Oct 24 '16 at 19:18
  • 1
    I know. But the answer to my question cannot be in the comments :) I cannot accept an answer that doesn't fufill the needs my question expressed. – Coder1000 Oct 24 '16 at 20:21
  • Please add the code concerning getting the scrollheight and loading elements number depending on height. I could do it myself, but I am not going to award the bounty to myself and a SO answer is not a full answer in this case without the code :) – Coder1000 Oct 25 '16 at 18:54