Helping you solve those tough coding problems!

Node.js – Ajax Pagination without the Pages Node.js – Ajax Pagination without the Pages

Published on Oct 15, 2012 by Jamie Munro

With my recent endeavours into Node, I thought that taking a fun article like this one - CakePHP 2-0 Ajax Pagination WITHOUT The Pages – would be a really fun experiment to see how difficult it would be to accomplish in Node.

At the end of the day, the logic is still the exact same, retrieve the items, determine the max length, calculate the number of pages, and then perform AJAX as well scroll down to fill in more content as-needed.

Leveraging the Express API and Jade templates, this is quite painless to implement into Node.

Before starting, it's important that you have a basic knowledge of Node, the Express API, and as well Jade templating.  If you don't, take a quick gander at these previous introductory articles I've written:

Now that you have the basics down, let's get started.  Firstly, let me layout the folder structure and files required to complete this example:












Most of the files should be pretty familiar from our previous examples.  The Jquery.min.js file can be included externally if you like or you can have a local copy included.  I'll provide examples for the remaining files.

Let's start with the app.js file:

var express = require('express'), jade = require('jade');
var app = express();

var fs = require('fs');
var items;
var itemsPerPage = 2;
var maxPages = 0;
fs.readFile('views/partials/items.json', 'utf8', function(error, data) {
items = JSON.parse(data);
maxPages = items.length / itemsPerPage;

var pub = __dirname + '/public';

app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.get('/', function(req, res) {
res.render('index.jade', {
pageTitle: 'Test',
items: items.slice(0, 2),
maxPages: maxPages,
layout: true

app.get('/page/:page', function(req, res) {
var page =;
var start = (page - 1) * 2;
var end = page * 2;

res.render('partials/items.jade', {
pageTitle: 'Test',
items: items.slice(start, end),
layout: false


Hopefully you are not immediately overwhelmed by the code, once we break it down a bit more, it should make a lot of sense.

Lines 1 and 2 are the same as before, simply setting up Express and Jade.  Line 4 through 11 are doing something a little new.  I'm including the ability to use the file stream (fs) and reading the aforementioned items.json file.  Once the file is read, the JSON values are stored in the local variable items.  Once loaded, I'm also calculating the maximum pages that are to be displayed by dividing by the number of items I want per page, in this case 2.

Lines 13 through 19 are simply configuring Express and Jade to work together as well as configuring the public folder to serve any Javascript or CSS files that we might have.

And finally, lines 21 through 40 are two routes that have been setup to render the view and display the items on screen.  In the first route (/), I'm simply passing the first two items of the items array to the view.  Whereas, in the second route, based on the page request parameter, I'm calculating the start and end position of the items to display.

Next, let's create this mysterious views/partials/items.json file:


{"title": "My first title", "body": "Lorem ipsum dolor sit …"},

{"title": "My second title", "body": "Lorem ipsum dolor sit …"},

{"title": "My third title", "body": "Lorem ipsum dolor sit …"},

{"title": "My fourth title", "body": "Lorem ipsum dolor sit …"},

{"title": "My fifth title", "body": "Lorem ipsum dolor sit …"},

{"title": "My sixth title", "body": "Lorem ipsum dolor sit …"},

{"title": "My seventh title", "body": "Lorem ipsum dolor sit …"},

{"title": "My eighth title", "body": "Lorem ipsum dolor sit …"},

{"title": "My ninth title", "body": "Lorem ipsum dolor sit …"},

{"title": "My tenth title", "body": "Lorem ipsum dolor sit …"}


This file contains some placeholder dummy data in a JSON array containing a title and body.  This will be used when I output the content.  In your own code, this would probably be something retrieved from your database…

Next, let's create the three view files mentioned above starting with the simplest, views/partials/items.jade:

for item in items
h2= item.title
p= item.body

This file outputs the title in an h2, the body in a p tag all wrapped in a div with the class of item and all wrapped in an outer div with the id of items.  The for loop is looping through the items variable that was passed to the layout from the app.js file.

Next is the basic views/index.jade file:

extends layout

block content
h2 Welcome
include partials/items

This file is pretty basic and just includes the previously created partial view.  The last view to create is the views/layout.jade file:

!!! 5
title= pageTitle
block scripts
var maxPages= #{maxPages};

#items { width: 300px; }
h1 Test
block content

The only noticeable thing happening above is on line 8 where I am creating a global variable called maxPages and setting it to the previously created maxPages variable that was calculated after loading the items.

You will also notice that I'm including a Javascript file called js/paging.js, this file should be created in your public directory as it contains the magic to perform the AJAX to request the next items:

var lastX = 0;
var currentX = 0;
var page = 1;

$(window).scroll(function () {
if (page < maxPages) {
currentX = $(window).scrollTop();
if (currentX - lastX > 200 * page) {
lastX = currentX;
$.get('/page/' + page, function(data) {

As a person scrolls down the page, I'm doing a calculation to determine if a new page should be loaded.  By keeping track of the last scroll position, the code is smart enough to not load additional content when the user scrolls up, and then back down again.

You can view the full source on GitHub here:

Tags: AJAX | jquery | pagination | JavaScript | jQuery | node

My Books
ASP.NET MVC 5 With Bootstrap and Knockout.js
Knockout.js Building Dynamic Client-Side Applications
20 Recipes for Programming MVC 3
20 Recipes for Programming PhoneGap
Rapid Application Development with CakePHP