Pagination Algorithm in JavaScript
I recently added pagination on two sites and in the process developed a solid pagination algorithm, which I wanted to share with you. To start, I read this article from KuraFire Network:
My take away from that article was the following pagination spec:
- should be functional, simple, and elegant
- should include prev/next and first/last as needed
- visible only when applicable, hidden otherwise
- don’t show prev when on the first page
- don’t show next when on the last page
- don’t show page numbers when only two pages (only show prev/next)
- don’t show ‘first’ when page ‘1′ is visible in pagination
- don’t show ‘last’ when the last page is visible in pagination
- clearly show the currently selected page (with the exception of the 2 page simplified view)
- when there are more pages than the maximum page #s shown, then the currently selected page should be intelligently centered
Pagination Spec Explained:
Probably, the only issue I deviated from the Pagination 101 article is that I do not show page numbers when there are only 2 pages. I found they added extra clutter and confusion, so I removed it. You really only have 2 options anyway and you are not adding (improving?) functionality by including the page numbers. ‘Prev’ and ‘Next’ are included because many users move directionally through pagination. ‘First’ and ‘Last’ are included (as needed), because in large result sets you need a way to get back to the beginning or end easily. Everything else is to improve simplicity and visibility of the selected page.
Below is an example of my algorithm. Keep in mind that ‘0′ actually refers to page ‘1′ and the last page is always ‘numpages - 1′; Most likely you will want to use this server-side, however, I rewrote it in JavaScript, because of copyright issues and this blog is about JavaScript. To sum up, if you follow the instructions in the method JavaDoc, you will create a string containing an unorder list containing the appropriate pagination to match what was passed in. Some additional explanation included below.
/**
* Create a pagination DOM or String as needed
* @param count {Integer} the number of results
* @param resultsPerPage {Integer} the maximum per page
* @param uri {String} the uri for the pagination
* @param offset {String} OPTIONAL: the offset of the current page
* @param sClass {String} OPTIONAL: the selected page class
* @return {String} the DOM string or empty string when DOM object is passed
*/
function buildPagination(count, resultsPerPage, uri, offset, sClass) {
if (! offset || 0 > offset) {offset=0;}
if (! isString(sClass)) {sClass = '';}
//
var str = [],
maxPages = 10,
halfMax = maxPages / 2,
numpages = Math.ceil(count / resultsPerPage),
index = Math.ceil(offset / resultsPerPage),
findex = index - halfMax,
lindex = index + halfMax;
//
// validate index
if (numpages - 1 < lindex) {lindex = numpages - 1;}
else if (lindex < maxPages) {lindex = maxPages - 1;}
if (0 > findex || index < maxPages - 1) {findex = 0;}
else {findex = numpages - maxPages;}
if (0 > index) {index = findex;}
if (numpages < index) {index = lindex;}
//
str.push('<ul>');
//
// if the first index is not 0 then the pagination view does not include page 1, so display first
if (findex) {
// logic to create the 'first' link
str.push('<li><a href="');
str.push(uri);
str.push('" rel="0">first</a></li>');
}
//
// if the index is not 0 then we are not on the first page, so display previous
if (index) {
// logic to create the 'previous' link
str.push('<li><a href="');
str.push(uri);
str.push('" rel="');
str.push(offset - resultsPerPage);
str.push('">first</a></li>');
}
//
// if there are more than 2 pages, then show page numbers, otherwise, just next/prev for simplicity
if (2 < numpages) {
for (var i=findex; i<=lindex; i++) {
// logic to create the page '#' link
if (i != index) {
str.push('<li><a href="');
str.push(uri);
str.push('" rel="');
str.push(offset);
str.push('">');
str.push(i);
str.push('</a></li>');
}
else {
str.push('<li class="');
str.push(sClass);
str.push('">');
str.push(i);
str.push('</li>');
}
}
}
//
// if the index is equal to the last index then we are not on the last page, so display next
if (lindex != index) {
// logic to create the 'next' link
str.push('<li><a href="');
str.push(uri);
str.push('" rel="');
str.push(offset + resultsPerPage);
str.push('">next</a></li>');
}
//
// if the last index is equals to (maxPages - 1) then the pagination view does not include the last page, so display last
if (lindex == maxPages-1) {
// logic to create the 'last' link
str.push('<li><a href="');
str.push(uri);
str.push('" rel="');
str.push((numpages - 1) * resultsPerPage);
str.push('">last</a></li>');
}
//
return str.join('');
}
More to come. In the process of writing a spreadsheet that steps through the algorithm with many different values so that you can see it manipulated.

I think there is a mistake. On the last line below:
// if there are more than 2 pages, then show page numbers, otherwise, just next/prev for simplicity
if (2
Comment by Phranck — July 31, 2008 @ 8:35 am
Well it looks like this piece of slop deleted all the code. XSS filtering for the win! Anyway it should be: if (i != index) and not: if (i == index)
Comment by Phranck — July 31, 2008 @ 8:36 am
You are quite right… as it was written you would only be able to click on the pagination link for the current page and all other page links would just be text. Thanks for catching that. I went ahead and fixed the typo.
Comment by Matt Snider — August 1, 2008 @ 9:07 am