Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Friday, July 13, 2007

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.

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

posted by Matt Snider at 6:29 pm  

3 Comments »

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

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

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

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress