This theme snippet provides an alternative for theming the pager.

One note: Due to Internet Explorer's incorrect handling of padding, you may have to manually adjust the IE Hacks section for each div a pager appears in that has a different padding value. In many sites the padding values don't change so it may not be a big problem.

Note: The following code is for themes that use the PHPTemplate theme engine. For themes that don't use the PHPTemplate this approach will have to be modified; It may be necessary to do a search and replace to change phptemplate_ to THEMENAME_.

The following goes in template.php


// This is the main theme override.
// one downside is that we ignore the $tags() because they don't fit
// the way we want to display. I don't think most theme('pager')s use them.
function phptemplate_pager($tags = array(), $page_size = 10, $element = 0, $attributes = array()) {
  global $pager_from_array, $pager_total;
  $output = '';

  // It's easier to calculate, to me at least, using page numbers
  if (($page_num = (ceil(($pager_from_array[$element] + 1) / $page_size))) < 1) {
    $page_num = 1;
  }

  // and number of pages
  if (($num_pages = (ceil(($pager_total[$element] + 1) / $page_size))) < 1) {
    $num_pages = 1;
  }

  // Display the pager.
  if ($pager_total[$element] > $page_size) {
    $output .= '<div class="pager-top">';
    $output .= _pager_prev($page_size, $element, $attributes, $page_num, $num_pages);
    $output .= _pager_next($page_size, $element, $attributes, $page_num, $num_pages);
    $output .= _pager_page_num($page_size, $element, $attributes, $page_num, $num_pages);
    $output .= '</div>';

    return $output;
  }
}

// This function creates a proper pager url.
function _pager_url($page_num, $text, $element, $attributes, $page_size) {
  global $pager_from_array;
  $from = ($page_num - 1) * $page_size;

  $from_new = pager_load_array($from, $element, $pager_from_array);
  
  return '<a href="/'. pager_link($from_new, $element, $attributes) . "\">$text</a>";
}

function _pager_page_num($page_size, $element, $attributes, $page_num, $num_pages) {
  return "<div class='pager-middle'>" . _pager_little_prev_button($page_size, $element, $attributes, $page_num, $num_pages) 
    . "<span class='page-num'>Page $page_num</span>" 
    . _pager_little_next_button($page_size, $element, $attributes, $page_num, $num_pages)
    . "</div>\n";

}

function _pager_little_prev_button($page_size, $element, $attributes, $page_num, $num_pages)
{
	// Is there a previous button to even print?
	// page < 1 == page 1.
	if ($page_num <= 1)
		return "";

  $prev = $page_num - 1;
	return _pager_url($prev, "&lt;&lt;", $element, $attributes, $page_size);
}

function _pager_little_next_button($page_size, $element, $attributes, $page_num, $num_pages)
{
	// Is there a previous button to even print?
	// page < 1 == page 1.
	if ($page_num >= $num_pages)
		return "";

  $next = $page_num + 1;
	return _pager_url($next, "&gt;&gt;", $element, $attributes, $page_size);
}

// function _pager_prevButton($url, $page, $page_size, $defaultPageSize, $count)
function _pager_prev($page_size, $element, $attributes, $page, $num_pages)
{
	// Is there a previous button to even print?
	// page < 1 == page 1.
	if ($page <= 1)
		return "";

	// First, let's just do the previous page.
	$prev = $page - 1;
	$string = _pager_url($prev, $prev, $element, $attributes, $page_size);

	// Let's do five pages, plus page #1, and a ... if there's a blank spot.
	for ($i = $page - 2; $i > max(1, ($page - 5)); $i--)
		$string = _pager_url($i, $i, $element, $attributes, $page_size) . " $string";

	if ($i != 1 && $i != 0) // if we stopped at something other than 1 there was > 5
		$string = "... " . $string;

  // Now see if we need to do powers of 10.
  // This is probably more complicated than it needs to be, and someone
  // clever could rewrite this -- but I got tired of fiddling with it.
	for ($counter = 1; $counter < 5; $counter++) {
		$pow = pow(10, $counter);
		if ($page > 16 * pow(10, $counter - 1))
			for ($i = floor((($page)/$pow) - 1) * $pow, $j = 0; $i > 1 && $j < 2; $i -= $pow, $j++)
				$string = _pager_url($i, $i, $element, $attributes, $page_size) . " $string";
	}

  // And finally, the very first page always shows up, unless we've already hit it.
	if ($prev != 1)
		$string = _pager_url(1, 1, $element, $attributes, $page_size) . " $string";

	return "<div class='pager-prev'>$string</div>";
}

function _pager_next($page_size, $element, $attributes, $page, $num_pages)
{
	// Is there a next button to even print?
	if ($page >= $num_pages)
		return "";

	// First, let's just do the previous page.
	$next = $page + 1;
	$string = _pager_url($next, $next, $element, $attributes, $page_size);
	// Let's do five pages, plus page #1, and a ... if there's a blank spot.

	for ($i = $page + 2; $i < min($num_pages, ($page + 5)); $i++)
		$string .= " " . _pager_url($i, $i, $element, $attributes, $page_size);
	if ($i != $num_pages && $i != $num_pages + 1) // if we stopped at something other than 1 there was > 5
		$string .= " ...";

	for ($counter = 1; $counter < 5; $counter++) {
		$pow = pow(10, $counter);
		if (($num_pages - $page) > 11 * pow(10, $counter - 1))
			for ($i = ceil(($page + 5)/$pow) * $pow, $j = 0; $i < $num_pages && $j < 2; $i += $pow, $j++)
				$string .= " " . _pager_url($i, $i, $element, $attributes, $page_size);
	}
	if ($next != $num_pages)
		$string .= " " . _pager_url($num_pages, $num_pages, $element, $attributes, $page_size);
	return "<div class='pager-next'>$string</div>";
 
}

And this goes in style.css.

.pager-top {
  margin-top: 1em;
  margin-bottom: 1em;
  position: relative;
}

.pager-middle {
  text-align: center;
  vertical-align: top;
  clear: none;
  position: relative;
  width: 100%;
  font-size: .8em;
  z-index: 0;
  top: 0;
}

.pager-prev {
  text-align: left;
  position: absolute;
  top: 0;
  left: 0;
  font-size: .8em;
  z-index: 2;
}

.pager-next {
  text-align: right;
  position: absolute;
  right: 0;
  float: right;
  font-size: .8em;
  z-index: 1;
}

/* IE hacks */
* html .main-content .pager-next {
  \margin-right: 5px;
  /* You may have to adjust this manually!!! */
}

* html .main-content td .pager-next {
  \margin-right: 0px;
}


.page-num {
  margin-left: .5em;
  margin-right: .5em;
}

Comments

wmostrey’s picture

A really simple pager, with no first/last link:

One note about this: you might notice that the $page_curr and $page_next have the same value. This is because the link ?page= is always 1 number below the actual page number. So if you're on the first page, you're actually on ?page=0. The second page is ?page=1. So to display the current page number, you need to do the page variable + 1. But to go to the next page, you need to do the same to put that value in the link again.

function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {
  global $pager_page_array, $pager_total;
  $page_prev = $pager_page_array[$element] - 1;
  $page_curr = $pager_page_array[$element] + 1;
  $page_next = $pager_page_array[$element] + 1;

  if ($pager_total[$element] > 1) {
    $output = '<div class="previous-next">';

    if ($pager_page_array[$element]!=0) $output.= '<a href="?page='.$page_prev.'" class="previous">Previous</a>';

    $output.= '<div class="previous-next-page">Page '.$page_curr.'/'.$pager_total[$element].'</div>';

    if ($page_curr!=$pager_total[$element]) $output.= '<a href="?page='.$page_next.'" class="next">Next</a>';

    $output.= '</div>';
    return $output;
  }
} 

You can theme it yourself, nothing fancy there.

mikemccaffrey’s picture

I added the first and last links back into the mix, and made it so that First, Prev, Next, and Last show up as text even if they are not going to be displayed as links.

I created more robust class specifications that will allow you to hide any part you don't want using CSS.


function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {

	global $pager_page_array, $pager_total;
	$page_prev = $pager_page_array[$element] - 1;
	$page_curr = $pager_page_array[$element] + 1;
	$page_next = $pager_page_array[$element] + 1;
	$page_last = $pager_total[$element] - 1;


	if ($pager_total[$element] > 1) {
	
		$output = '<div class="pager">';

    	if ($pager_page_array[$element]!=0) {
			
			$output.= '<span class="pager-first pager-first-active"><a href="?page=0">First</a></span>';
			
			$output.= '<span class="pager-previous pager-previous-active"><a href="?page='.$page_prev.'">Prev</a></span>';
		
		} else { 
		
			$output.= '<span class="pager-first pager-inactive pager-first-inactive">First</span>';
			
			$output.= '<span class="pager-previous pager-inactive pager-previous-inactive">Prev</span>';

		}

		$output.= '<span class="pager-pagenumbers">Page '.$page_curr.'/'.$pager_total[$element].'</span>';


    	if ($page_curr!=$pager_total[$element]) {
		
			$output.= '<span class="pager-next pager-next-active"><a href="?page='.$page_next.'">Next</a></span>';
		
			$output.= '<span class="pager-last pager-last-active"><a href="?page='.$pager_total[$element].'">Last</a></span>';
    
		} else {
		
			$output.= '<span class="pager-next pager-inactive pager-next-inactive">Next</span>';

			$output.= '<span class="pager-last pager-inactive pager-last-inactive">Last</span>';
		}

		$output.= '</div>';
   
		return $output;
	}
}

Hope it helps!

mikemccaffrey’s picture

After I wrote the code above, my client decided that they still wanted the clickable numbers between the Prev and Next links, so I wrote some code to handle that.

Just take this line from my code above:

 $output.= '<span class="pager-pagenumbers">Page '.$page_curr.'/'.$pager_total[$element].'</span>';

and replace it with this:

 $output.= '<span class="pager-pagenumbers">';

$num_page_links = 5;

if($pager_total[$element] <= $num_page_links) { 

	$pagenumbers_start = 1; 
	
	$pagenumbers_end = $pager_total[$element];

	
} else {
	
	if($page_curr <= ceil($num_page_links/2)) {

		$pagenumbers_start = 1; 
	
		$pagenumbers_end = $num_page_links;
	
	
	} else if ($page_curr >= $pager_total[$element] - floor($num_page_links / 2)) {

		$pagenumbers_start = $pager_total[$element] - $num_page_links + 1; 
	
		$pagenumbers_end = $pager_total[$element];
	
	
	} else {
			
		$pagenumbers_start = $page_curr - floor(($num_page_links - 1) / 2); 
		
		$pagenumbers_end =  $page_curr + ceil(($num_page_links - 1) / 2); 
		
	}

}

if($pagenumbers_start > 1) $output .= ' <span class="pager-inactive pager-pagenumbers-inactive">...</span> ';

for($i = $pagenumbers_start; $i <= $pagenumbers_end; $i++) {

	if($i == $page_curr) $output .= ' <span class="pager-inactive pager-pagenumbers-inactive">'.$page_curr.'</span> ';

	else $output .= ' <a href="?page='.($i-1).'">'.$i.'</a> ';

}

if($pager_total[$element] > $pagenumbers_end) $output .= ' <span class="pager-inactive pager-pagenumbers-inactive">...</span> ';


$output.= '</span>';


Obviously, this is a bit more complicated than just displaying what page you are on, and I admit that this code is pretty messy and repetitive, but it seems to work alright. You can even specify the number of page links you want to show at the top, and any additional pages are hidden with an ellipsis.

field4000’s picture

I just want to say a big thank you for your snippet. I found it after many days of looking.

It is exactly what I was looking for.

Cheers for your efforts!

guidot’s picture

function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {
  global $pager_page_array, $pager_total;
  $page_prev = $pager_page_array[$element] - 1;
  $page_curr = $pager_page_array[$element] + 1;
  $page_next = $pager_page_array[$element] + 1;
  $page_last = $pager_total[$element] - 1;
  if ($pager_total[$element] > 1) {
    $output = '<div class="pager">';
    if ($pager_page_array[$element]!=0) {
      $output.= '<span class="pager-first pager-first-active"><a href="?page=0">'.t('« first').'</a></span>';
      $output.= '<span class="pager-previous pager-previous-active"><a href="?page='.$page_prev.'">'.t('‹ previous').'</a></span>';
    } else {
      $output.= '<span class="pager-first pager-inactive pager-first-inactive">First</span>';
      $output.= '<span class="pager-previous pager-inactive pager-previous-inactive">Prev</span>';
    }
    $output.= '<span class="pager-pagenumbers">'.t('page ').$page_curr.t(' of ').$pager_total[$element].'</span>';
    if ($page_curr!=$pager_total[$element]) {
      $output.= '<span class="pager-next pager-next-active"><a href="?page='.$page_next.'">'.t('next ›').'</a></span>';
      $output.= '<span class="pager-last pager-last-active"><a href="?page='.$pager_total[$element].'">'.t('last »').'</a></span>';
    } else {
      $output.= '<span class="pager-next pager-inactive pager-next-inactive">Next</span>';
      $output.= '<span class="pager-last pager-inactive pager-last-inactive">Last</span>';
    }
    $output.= '</div>';
    return $output;
  }
}
ozzyisti’s picture

Hi mikemccaffrey!

First of all, thank you for the code, it helped me a lot.
I found a small bug. Nothing serious, but I thought it will save some time for other developers using your code.
Instead of this line : $output.= '<span class="pager-last pager-last-active"><a href="?page='.$pager_total[$element].'">Last</a></span>';
you should use : $output.= '<span class="pager-last pager-last-active"><a href="?page='.$page_last.'">Last</a></span>';
else when clicking on the "last" link, nothing will appear, because the page number is incorrect.

Hope it helps!

Fiasst’s picture

Thanks guys, this is a very nice snippet. However, when using the smaller custom pager on a view with filters, the pager won't use the filters querystrings. I've written a little extra code. (My first template.php code):

function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {
	global $pager_page_array, $pager_total;
	$page_prev = $pager_page_array[$element] - 1;
	$page_curr = $pager_page_array[$element] + 1;
	$page_next = $pager_page_array[$element] + 1;
	
	# get querystrings (except q="" and page="")
	$cgi = $_SERVER['REQUEST_METHOD'] == 'GET' ? $_GET : $_POST;
	$query = '';
	foreach ($cgi as $key => $val) {
		if ($key != 'page' && $key != 'q') {
			$query .= '&'. $key .'='. $val;
		}
	}
	$query = substr($query, 1);
	
	if ($pager_total[$element] > 1) {
		$output = '<div id="pager-wrap">';
		if ($pager_page_array[$element]!=0) $output.= '<a href="?page='.$page_prev.'?'.$query.'" class="previous">&#8249;</a>';
		$output.= '<div class="pagecount">Page '.$page_curr.' / '.$pager_total[$element].'</div>';
	
		if ($page_curr!=$pager_total[$element]) $output.= '<a href="?page='.$page_next.'?'.$query.'" class="next">&#8250;</a>';
		$output.= '</div>';
		return $output;
	}
}

Realworks Media Website Design

beekay1076’s picture

Thanks, this is great.

For anyone interested, I've implemented it with images for the previous and next links.
Just added the theme_image function where the link text was

<?php
function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {
    global $pager_page_array, $pager_total;
    $page_prev = $pager_page_array[$element] - 1;
    $page_curr = $pager_page_array[$element] + 1;
    $page_next = $pager_page_array[$element] + 1;
   
    # get querystrings (except q="" and page="")
    $cgi = $_SERVER['REQUEST_METHOD'] == 'GET' ? $_GET : $_POST;
    $query = '';
    foreach ($cgi as $key => $val) {
        if ($key != 'page' && $key != 'q') {
            $query .= '&'. $key .'='. $val;
        }
    }
    $query = substr($query, 1);
   
    if ($pager_total[$element] > 1) {
        $output = '<div id="pager-wrap">';
        if ($pager_page_array[$element]!=0) $output.= '<a href="?page='.$page_prev.'?'.$query.'" class="previous">' . theme('image', path_to_theme().'/images/pager-prev.jpg', 'Previous', 'Click to see the previous set of results', array('border' => 0), TRUE) . '</a>';
        $output.= '<div class="pagecount">Page '.$page_curr.' / '.$pager_total[$element].'</div>';
   
        if ($page_curr!=$pager_total[$element]) $output.= '<a href="?page='.$page_next.'?'.$query.'" class="next">' . theme('image', path_to_theme().'/images/pager-next.jpg', 'Next', 'Click to see the next set of results', array('border' => 0), TRUE) . '</a>';
        $output.= '</div>';
        return $output;
    }
}
?>

Cheers!

ValerieCBL’s picture

Hi Guys!

I totally agree with the first person's post about his "not so humble opinion" of how the page numbers, the next, previous, etc., looks pretty bad, although works brilliantly. I was just wondering if the code you posted to put in the template.php file will work if placed in the pager.inc file?

I'm working in Drupal 6.2 and I'm not sure if I should be placing your wonderful code in the template.php or the pager.inc file? Maybe I could put it in a pager.php file? I've heard that not all include files need to end in .inc!?? I'm unsure if I put it in the template.php file, will it override the pager.inc file? If not, I'll need to place the code in pager.inc and will I be able to import the .css file into the .inc file so that it can read it?

Of course, if I could get away with pager.php in the includes folder, then it should be OK... but do I then delete the pager.inc? Sorry for this convoluted question... I'm not too familiar with these .inc files! I'm not sure if you will or anyone else is gonna get this... last post on this thread was in May, but I thought I'd try anyways.

Thank you,
Valerie :)
Cheeseburgerlocker.com

PixelClever’s picture

Template.php (and the rest of the Drupal api) exist to avoid having to hack core. If you change an include file you will be causing yourself trouble every time there is an essential upgrade. This is, I think, the single most important rule of Drupal... never hack core. (Unless of course you are an old pro helping develop the next Drupal release which is totally different.)

Drupal Theming and site design http://www.PixelClever.com

jaskegreen’s picture

What would I need to alter if I wanted, say, a solid circle to represent the current page? I would then have empty circles represent the previous and next links.

Thanks for the help.

rajesh_1988’s picture

Hi,
actually i have created image gallery. here i need to modify the default pager that will look like next.png prev.png(image).i don't want
pager numbers and first and last .i am working on drupal 7.x.is this code useful for me?where should i past this code?

jjjames’s picture

Cool, I may give this a shot!