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 KuraFire Network's Pagination 101 article.

My take away from that article was the following pagination spec:

  1. should be functional, simple, and elegant
  2. should include prev/next and first/last as needed
  3. visible only when applicable, hidden otherwise
  4. don’t show prev when on the first page
  5. don’t show next when on the last page
  6. don’t show page numbers when only two pages (only show prev/next)
  7. don’t show first when page 1 is visible in pagination
  8. don’t show last when the last page is visible in pagination
  9. clearly show the currently selected page (with the exception of the 2 page simplified view)
  10. 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 deviation from the article is that I do not show page numbers when there are only two pages. I found they added extra clutter and confusion, so removed it. There are only two options anyway and the extra page numbers are not adding (improving?) functionality. The Prev and Next links are included because most users move directionally through the pagination. The First and Last links 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 iNumPages - 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 unordered list containing the appropriate pagination to match what was passed in. Some additional explanation included below.

function buildPagination(iCount, iResultsPerPage, sUri, iOffset, sClass) {
	if (!iOffset || 0 > iOffset) {
	    iOffset = 0;
	if (! isString(sClass)) {
	    sClass = '';
	var sb = [],
		iMaxPages = 10,
		iHalfMax = Math.floor(iMaxPages / 2),
		iNumPages = Math.ceil(iCount / iResultsPerPage),
		iCurrIndex = Math.ceil(iOffset / iResultsPerPage),
		iFirstIndex = iCurrIndex - iHalfMax,
		iLastIndex = iCurrIndex + iHalfMax;

	// validate iCurrIndex
	if (iNumPages - 1 < iLastIndex) {iLastIndex = iNumPages - 1;}
	else if (iLastIndex < iMaxPages) {iLastIndex = iMaxPages - 1;}
	if (0 > iFirstIndex || iCurrIndex < iMaxPages - 1) {iFirstIndex = 0;}
	else {iFirstIndex = iNumPages - iMaxPages;}
	if (0 > iCurrIndex) {iCurrIndex = iFirstIndex;}
	if (iNumPages < iCurrIndex) {iCurrIndex = iLastIndex;}

    '); // if the first iCurrIndex is not 0 then the pagination view does not include page 1, so display first if (iFirstIndex) { // logic to create the first link sb.push('
  • first
  • '); } // if the iCurrIndex is not 0 then we are not on the first page, so display previous if (iCurrIndex) { // logic to create the previous link sb.push('
  • first
  • '); } // if there are more than 2 pages, then show page numbers, otherwise, just next/prev for simplicity if (2 < iNumPages) { for (var i = iFirstIndex; i <= iLastIndex; i++) { // logic to create the page # link if (i !== iCurrIndex) { sb.push('
  • '); sb.push(i); sb.push('
  • '); } else { sb.push('
  • '); sb.push(i); sb.push('
  • '); } } } // if the iCurrIndex is equal to the last iCurrIndex then we are not on the last page, so display next if (iLastIndex !== iCurrIndex) { // logic to create the next link sb.push('
  • next
  • '); } // if the last iCurrIndex is equals to (iMaxPages - 1) then the pagination view does not include the last page, so display last if (iLastIndex === iMaxPages - 1) { // logic to create the last link sb.push('
  • last
  • '); } return sb.join(''); }

Hope this helps. Feel free to adopt this to other languages as necessary.