Currently Browsing: Home » Under the Hood: Custom WordPress Pagination System

Under the Hood: Custom WordPress Pagination System

If you use WordPress, you know that after a few months of blogging, there are far too many posts to display simultaneously. To reduce the loading time of your website, you could consider a pagination system, in which you display only a certain number of posts per page. By giving the user the ability to navigate through pages, your posts will all be available and left intact.

Pagination

Pagination? How?

Unfortunately, changing the posts per page option under your WordPress reading settings does not automatically create pagination; rather, you have two options:

  1. Use WordPress’ in-built functions (posts_nav_link, previous_posts_link and/or next_posts_link) to allow the user to navigate between pages.
  2. Create your own pagination system.

The main problem with the first option is that WordPress only allows the user to go to the next or previous page; there is no way to jump a set of pages without having to visit everything in-between. Consequently, the bottleneck is in the user-experience.

With the second option, the burden is on the developer. Even though it requires more coding, the user experience is not compromised. As a result, today’s article will focus on how to write your own custom WordPress pagination system. To view a demo, see Lateral Code’s pagination.

Specifications

Before we begin coding, let’s lay out our specifications. Our pagination system will ensure that the user can visit the first and last page. It will also provide links to pages within a certain range of the current page. We will call this the scope.

For example, if we have twenty pages, our scope is set to two, and the user is currently on page 5, our pagination would look like this:

1	...	3	4	5	6	7	...	20

Note that the ellipses (…) are just used as visual indicators of a jump in page number.

Since three, four, six, and seven are two of less numbers away from five, they are displayed. In like manner, if we had our scope to set to one, the pagination would be:

1	...	4	5	6	...	20

In this example, three and seven are removed because their range from our current page number, five, is two. This is greater than the scope of one.

Although this system seems to work, what if the scope is two and the current page number is one? No previous page exists to link to. As a result, we can just add more pages to the other side:

1	2	3	4	5	...	20

Since we missed two pages to the left of one, we added them to the right. That’s why pages four and five are visible. Using the same principal, if our current page is twenty, the pagination would be:

1	...	16	17	18	19	20

In this case, the pages are added to the left since they can’t be placed to the right.

Code

Now that our specification is complete, let’s begin coding. We will create a pagination function in functions.php which will be later used in our index.php template. The scope, or range, will have a default value of two:

function pagination( $scope = 2 )

Our first task is to find the number of pages and the current page:

global $wp, $posts_per_page;

$numPages = (int) ( wp_count_posts()->publish / $posts_per_page );
if( $numPages < = 1 )
	return; // no need for pagination with one page

$queryVars = $wp->query_vars;
$curPage = isset( $queryVars[ 'paged' ] ) ? (int) $queryVars[ 'paged' ] : 1;

wp_count_posts()->publish represents the total number of published posts. $posts_per_page is the posts per page setting under the reading area of the WordPress dashboard. Dividing the former by the latter will give us the number of pages.

To find the current page, we can use the $wp->query_vars array. If the paged key exists, its value will be the page. Otherwise, the user is on page one.

After finding the statistics, we need to create the pagination. Although we know that the first and last page will be included, the middle has yet to be found:

// page bounds
$start = $curPage - $scope;
$end = $curPage + $scope;

// if we can't satisfy the scope (add enough pages) on one side,
// add pages to the other side
if( $start < = 1 ) {
	$end += ( 1 - $start );
	$start = 2;
}
else if( $end >= $numPages ) {
	$start -= ( $end - $numPages );
	$end = $numPages - 1;
}

// limit the start and end to their extreme values
$start = max( $start, 2 );
$end = min( $end, $numPages - 1 );

$start and $end correspond the left-most and right-most page value, respectively. The if block is used to display more pages on one side if they cannot be added to the other, as per the last two examples of the specification. Finally, $start and $end are capped at their boundary values to ensure they are within range.

Now that we have the pages, we can aggregate them into an array:

$pagesToLinkTo = array( 1 );
for( $page = $start; $page < = $end; $page++ )
	$pagesToLinkTo[] = $page;
$pagesToLinkTo[] = $numPages;

Finally, we have to process the array and output list elements corresponding to each page number:

$prevPage = $pagesToLinkTo[0];
foreach( $pagesToLinkTo as $page ) {
	if( $page - $prevPage > 1 ) // skipped a few pages
		echo '<li>...</li>'; // add a spacer

	// echo the link
	echo '<li><a href="' . get_bloginfo( 'home' ) . '/page/' . $page . '">' . $page . '</a></li>';
	$prevPage = $page;
}

The URL of each page is http://example.com/page/number, where http://example.com is the home of the WordPress site and number is the page number. We link to this URL for each page that we output.

To determine whether we need to add ellipses, we compare the current page with the previous page. If the difference is greater than one, we skipped a few pages and have to add the spacer.

Here is the pagination function as a whole:

function pagination( $scope = 2 ) {
   	global $wp, $posts_per_page;

	$numPages = (int) ( wp_count_posts()->publish / $posts_per_page );
	if( $numPages < = 1 )
		return; // no need for pagination

	$queryVars = $wp->query_vars;
	$curPage = isset( $queryVars[ 'paged' ] ) ? (int) $queryVars[ 'paged' ] : 1;

	// page bounds
	$start = $curPage - $scope;
	$end = $curPage + $scope;

	// if we can't satisfy the scope (add enough pages) on one side,
	// add pages to the other side
	if( $start < = 1 ) {
		$end += ( 1 - $start );
		$start = 2;
	}
	else if( $end >= $numPages ) {
		$start -= ( $end - $numPages );
		$end = $numPages - 1;
	}

	// limit the start and end to their extreme values
	$start = max( $start, 2 );
	$end = min( $end, $numPages - 1 );

	$pagesToLinkTo = array( 1 );
	for( $page = $start; $page < = $end; $page++ )
		$pagesToLinkTo[] = $page;
	$pagesToLinkTo[] = $numPages;

	$prevPage = $pagesToLinkTo[0];
	foreach( $pagesToLinkTo as $page ) {
		if( $page - $prevPage > 1 ) // skipped a few pages
		echo '<li>...</li>'; // add a spacer

		// echo the link
		echo '<li><a href="' . get_bloginfo( 'home' ) . '/page/' . $page . '">' . $page . '</a></li>';
		$prevPage = $page;
	}
}

Wrapping Up

To finalize our system, we’ll have to call this function in our index.php template:

<ul id="pagination">
	<?php pagination(); ?>
</ul>

Well, that was easy, eh? Remember to style the pagination in accordance to your site’s design. To view a demo, see the bottom of Lateral Code’s home page.

Thanks for reading!

Tags: ,

This entry was posted on Sunday, November 7th, 2010 at 19:07:36. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Leave a Reply

Want to be notified when someone replies? Subscribe to this post's comment RSS feed.
Any field marked with a * is required.