Customizing the pager (next, previous navigation)

Last modified: June 20, 2009 - 18:09

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

<?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;
}

Customize the pager: another example

wmostrey - March 30, 2007 - 08:11

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.

<?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;

  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.

A slightly altered version of wmostrey's example

mikemccaffrey - March 25, 2008 - 21:31

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.

<?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;
   
$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!

Using Page Numbers

mikemccaffrey - March 25, 2008 - 21:40

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:

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

and replace it with this:

<?php
$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.

Thanks!

field4000 - April 2, 2009 - 07:14

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!

Translateable version

guidot - April 9, 2009 - 17:24

<?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;
 
$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;
  }
}
?>

One addition..

Fiasst - November 28, 2007 - 18:18

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):

<?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">‹</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">›</a>';
       
$output.= '</div>';
        return
$output;
    }
}
?>

Realworks Media Website Design

And with images

beekay1076 - May 30, 2008 - 04:29

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!

What about pager.inc?

ValerieCBL - November 10, 2008 - 04:04

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

Never hack core

Aaron Hawkins - February 14, 2009 - 13:18

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

 
 

Drupal is a registered trademark of Dries Buytaert.