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:

Pagination 101

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 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.

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('
    '); // 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('
  • first
  • '); } // 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('
  • first
  • '); } // 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('
  • '); str.push(i); str.push('
  • '); } else { str.push('
  • '); str.push(i); str.push('
  • '); } } } // 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('
  • next
  • '); } // 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('
  • last
  • '); } 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.