Template to List Posts By First Letter Of Title

Several persons have asked for a template to list posts or pages by the first letter of the title. The code below is set for posts, but is easily modified for pages or custom posts. The CSS will need to be modified to fit your own theme.

This template lists a set number of post titles per page.  See this Tip for a template that lists one letter per page.

Here is a screenshot of the first page:

Here is the pastebin link for the code below.

<?php
/*
Template Name: A-Z Pages

A WordPress template to list page titles by first letter.

You should modify the CSS to suit your theme and place it in its proper file.
Be sure to set the $posts_per_row and $posts_per_page variables.
*/
$posts_per_row = 3;
$posts_per_page = 15;
?>

<?php get_header(); ?>

<style type="text/css">
.letter-group { width: 100%; }
.letter-cell { width: 5%; height: 2em; text-align: center; padding-top: 8px; margin-bottom: 8px; background: #e0e0e0; float: left; }
.row-cells { width: 70%; float: right; margin-right: 180px; }
.title-cell { width: 30%;  float: left; overflow: hidden; margin-bottom: 8px; }
.clear { clear: both; }
</style>

<div id="main-background">

   <div id="main-column">
      <h1><?php the_title(); ?></h1>

      <div class="margin-top"></div>

      <div id="a-z">

         <?php
         $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
         $args = array (
            'posts_per_page' => $posts_per_page,
            'post_type' => 'post',
            'orderby' => 'title',
            'order' => 'ASC',
            'paged' => $paged
         );
         query_posts($args);
         if ( have_posts() ) {
            $in_this_row = 0;
            while ( have_posts() ) {
               the_post();
               $first_letter = strtoupper(substr(apply_filters('the_title',$post->post_title),0,1));
               if ($first_letter != $curr_letter) {
                  if (++$post_count > 1) {
                     end_prev_letter();
                  }
                  start_new_letter($first_letter);
                  $curr_letter = $first_letter;
               }
               if (++$in_this_row > $posts_per_row) {
                  end_prev_row();
                  start_new_row();
                  ++$in_this_row;  // Account for this first post
               } ?>
               <div class="title-cell"><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></div>
            <?php }
            end_prev_letter();
            ?>
            <div class="navigation">
               <div class="alignleft"><?php next_posts_link('&laquo; Higher Letters') ?></div>
               <div class="alignright"><?php previous_posts_link('Lower Letters &raquo;') ?></div>
            </div>
         <?php } else {
            echo "<h2>Sorry, no posts were found!</h2>";
         }
         ?>

      </div><!-- End id='a-z' -->

   </div><!-- End class='margin-top -->

</div><!-- End id='rightcolumn' -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

<?php
function end_prev_letter() {
   end_prev_row();
   echo "</div><!-- End of letter-group -->\n";
   echo "<div class='clear'></div>\n";
}
function start_new_letter($letter) {
   echo "<div class='letter-group'>\n";
   echo "\t<div class='letter-cell'>$letter</div>\n";
   start_new_row($letter);
}
function end_prev_row() {
   echo "\t</div><!-- End row-cells -->\n";
}
function start_new_row() {
   global $in_this_row;
   $in_this_row = 0;
   echo "\t<div class='row-cells'>\n";
}

?>

64 Responses to Template to List Posts By First Letter Of Title

  • Denis says:

    Hi there. I was wondering if i could use the same script but instead of posts with categories?
    I want to make a category index like yours. Thank you.

  • Joaquin says:

    Hi Mac!
    This works great but, unfortunately I have a lot of posts that start with “a”, “an” and “the”, is there a way to make them, for these cases, get listed on the first letter of the second word?
    Thanks!

    • macadmin says:

      This can be done, but the code is too complex to be added here. I will post a pastebin that shows how to do this after I have time to test the solution.

      • macadmin says:

        Here is the pastebin for the code: http://pastebin.com/JxKv5RDx

        • Joaquin says:

          Awesomeness! It’s perfect!
          If I want to add other prefixes to the list, like “le ” or “la ” is it just a matter of copying this:

          IF($wpdb->posts.post_title REGEXP(‘^the ‘),CONCAT(SUBSTR($wpdb->posts.post_title,5), ‘, ‘, SUBSTR($wpdb->posts.post_title,1,4)),

          and changing ^the for ^le ?

          • Mac McDonald says:

            Almost that simple, but you need to adjust the numbers in the SUBSTR functions. Check the differences between ‘^the ‘ and ‘^an ‘.

            And, you must add another closing parenthesis just before ‘AS sort_title’.

  • That actually was the complete error code. If I take out that line of code that gives the error, I get the same error with $args instead of $paged. If it helps, I’m using the Sahifa Theme

  • Hey Mac thanks for the great snippet. However, i get this error returned:
    Parse error: syntax error, unexpected ‘$paged’ (T_VARIABLE) in /site/Page-Database.php on line 34
    Is there something I’m not doing right?

  • tim says:

    Great work, easy to apply and customize, thanks very much!!
    But is there an easy way, how to display posts AND pages, instead of just either or?

  • Mizukamakiri says:

    Hi and thanks for an awesome snippet.

    For some reason it is showing me a list of pages, rather than posts. Any idea why?

  • Mat says:

    Hi Mac, Thanks for the great code – its just what i’ve been looking for
    i’m trying to add small thumbnails alongside each link taken from the post’s “featured image”- any ideas as to how to do this simply.
    Thanks
    Mat

    • Mac McDonald says:

      It is pretty easy. Just change this line:

      <div class="title-cell"><a href="<?php the_permalink() ? rel="nofollow">" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></div>
       

      to this:

                     <div class="title-cell">
                        <?php if ( has_post_thumbnail() ) { ?>
                           <div class="post-thumbnail">
                              <?php the_post_thumbnail( array(80,80) ); ?>
                           </div>
                        <?php } ?>
                        <a href="<?php the_permalink() ? rel="nofollow">" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a>
                     </div>
      

      You will need to add CSS for .post-thumbnail to float it left and add some spacing, something like this:

      .post-thumbnail { float: left; margin-right: 10px; }
      

      The ‘array(80,80)’ in the_post_thumbnail() sets the size of the image.

  • Ruriko says:

    I want to list all posts instead of paginating. How can I do this?

  • Adrián says:

    Hi Mac! Thanks for this code, I know it’s three years later but I’m using it now and it works perfect, but when I modify a post or add a new one it came to the end of the list, any idea why??? here is the problem http://aristu.com/lista/

    • Mac McDonald says:

      I can’t say for sure, but I suspect that the later post title is in a different charcter set code than the other posts. It may be in a multi-byte code set.

      • Adrián says:

        Hi Marc, you are right,
        it started when I migrate the database so I assume that I changed the code set, if I change the name of anyone and save it, it comes down, you can see in the link.

        One ñast question, I don’t know why I arrived to put numbers last but now, when I change the name and save it, it doesn’t move down. By default your code put numbers on top, you know how to change it?

        Thnk!

        • Mac McDonald says:

          It looks like any character in the new code set will sort higher than all characters in the old set. So, numbers in the new set will come after all posts in the old set.

          The Codex has an article on changing database character sets: http://codex.wordpress.org/Converting_Database_Character_Sets

          • Adrián says:

            Thanks Mac, the problem it’s that I couldn’t depend on the new or old set cause they will create new post and all will be in the new set, so numbers will came to top, I was thinking about something in the order of the query post…I will work on it cause I tried before bu without any good result :)

            Thanks!

          • Mac McDonald says:

            The Codex article tells how to convert all the old set to the new one so that both old and new posts will be the same.

  • Ben says:

    Really useful snippet, much better that what I came up with – using an array of letters and displaying none if empty :)

  • angue says:

    thanks for this code i wonder how to do this for tags. actually i’im trying to create an index page with a list of article by tags just like this one http://www.chefnini.com/index-des-recettes/.
    any help will be much appreciate thanks

  • Lisa says:

    Is it possible to display also the letters under which there is no post yet? So that I can see complete A-Z even if at some letters there is no post yet? Thanks,
    Lisa

    • Mac says:

      It might be possible, but not easy for a couple of reasons.

      First, titles don’t always start with a letter and you probably don’t want to list ‘No Pages’ for non-alphabetic characters.

      The more significant problem is the way pages are handled. You would need to pass the last letter on a page to the next page to know if any letters were skipped. As it is, the current letter is reset on every page so that the letter heading will always print for the first letter.

  • Lisa says:

    Hey Mac, you are seriously the only wp code expert I have ever seen whose work works so perferctly well. Great job! Lisa

  • Majed says:

    Thank you very much Mac. You are great asst to the WordPress community

  • Majed says:

    Good day,
    Letter navigation "Higher and lower Letters" is added at the bottom of the page only. I want it to be at top and bottom of the page, how I can do that.

    • Mac says:

      Try adding these lines just after the 'if (have_posts()) {' line (line number 43):

      ?>
      <div class="navigation">
          <div class="alignleft"><?php next_posts_link('&laquo; Higher Letters') ?></div>
          <div class="alignright"><?php previous_posts_link('Lower Letters &raquo;') ?></div>
      </div>
      <?php
      
  • Daniel says:

    I have it changed from the start , I have only added this to array:
    ‘cat’ => 907, but does it work for posts too ? I don’t use this code for pages ,
    here on this page it doesn’t work even if there are many posts in subcategories and even if I insert subcategory with posts: http://www.4ella.com/clubs/sardegna/ , here it works if I insert ITALY as a parent category (I have about 800 posts=clubs there): http://www.4ella.com/clubs/locali-notturni/ , both pages are the same code , I have only changed category , in first case 22= 907 , in second case 22=245 and it works , it is really strange :-(

  • Daniel says:

    Thank you very much MAC for you very quick respond , very strangely this gives me No POSTS were found message , even if I have there posts in subcats and even if I insert directly subcat with posts , maybe some slow or bad response from the server ? It is really strange , I go sleep now and will try it tomorrow , thanks again Daniel

  • Daniel says:

    The question about displaying posts from only 1 parent category should be here so I will repeat it here again :
    Mac how can I display only posts from one category and their subcategories ? I have about 500 categories so better than exclude 400 categories I would like to include one category which contains 100 subcategories below , I need to get only posts from one main category and their subcats.

    • Mac says:

      Assuming your parent category id is 22, add this line to the $args array:

      'cat' => 22,

               $args = array (
                  'cat' => 22,
                  'posts_per_page' => $posts_per_page,
                  'post_type' => 'page',
                  'orderby' => 'title',
                  'order' => 'ASC',
                  'paged' => $paged
               );
      
  • Catiebug says:

    Is it possible to grab the first letter in a specific meta value instead of the post title?

    • Mac says:

      Yes, you can do that fairly easily. Just change the appropriate section of the original code to look like the following. This is UNTESTED, so you may need to make some adjustments.

               $my_meta_key = 'The Meta Key';
               $args = array (
                  'posts_per_page' => $posts_per_page,
                  'post_type' => 'post',
                  'orderby' => 'meta_value',
                  'meta_key' => $my_meta_key,
                  'order' => 'ASC',
                  'paged' => $paged
               );
               query_posts($args);
               if ( have_posts() ) {
                  $in_this_row = 0;
                  while ( have_posts() ) {
                     the_post();
                     $meta_val = get_post_meta( $post->ID,$my_meta_key,true);
                     $first_letter = ($meta_val) ? substr($meta_val,0,1) : ' ';
      
  • Daniel says:

    What a very quick respond !! perfect work :-)
    Mac you have to remove the space in your code, remove the space before the word “caller” , otherwise it doesn’t work.
    instead of
    ‘ caller_get_posts’ => 1
    should be :
    ‘caller_get_posts’ => 1
    Last question : is it hard to implement per letter pagination?
    If someone has a lot of posts , it should be more convenient instead of “higher letters” to have
    A B C D E F G H I J K L M N O P Q ……………………. pagination , to have a possibility to click on single letter.

    • Mac says:

      Thanks for catching the space!

      Pagination by letter would require a substantial rewrite. I will take a look to see what exactly is required and post a new topic if I get it working.

      • Daniel says:

        Yes I am aware that it requires total rewrite code , in case you will manage to get it work I am looking forward for that -:) should be nice to use the javascript for that pagination, I will watch this site .

  • Daniel says:

    Thank you very much , I am very happy that i have found this website , it’s working perfectly but when You have a sticky post , it puts it before all alphabet letters :-( , how can we exclude sticky posts ? you can see it here: http://www.4ella.com/clubs/italia/a-z/

    • Mac says:

      To ignore the sticky property, just add the ‘caller_get_posts’ argument like this (don’t forget the comma after the paged argument!):

               $args = array (
                  'posts_per_page' => $posts_per_page,
                  'post_type' => 'page',
                  'orderby' => 'title',
                  'order' => 'ASC',
                  'paged' => $paged,
                  'caller_get_posts' => 1
               );
      
  • Storm says:

    Hello,
    great code, but stupid question on my behalf; I have added all the coding in, eddited it and put in the extra coding so it only works on my business directory page with posts as eash busniness, BUT ITS NOT!!!!!!!!!
    Here is my code i have edited! where have i gone wrong??

    business-directory;
    $paged = (get_query_var(‘paged’)) ? get_query_var(‘paged’) : 1;
    $args = array (
    ‘post_parent’ => $business-directory,
    ‘posts_per_page’ => $posts_per_page,
    ‘post_type’ => ‘post’,
    ‘orderby’ => ‘title’,
    ‘order’ => ‘ASC’,
    ‘paged’ => $paged
    );

    • Mac says:

      This looks like it is too complex to be handled in these replies. Please use the Contact Me form so we can email each other.

  • Ginocolada says:

    Ah! Great!

    THANX! I’m supporting (volunteer) -> a dutch http://plantencatalogus.nl (plantcatalogue) with this code.

    Simplicity, the power of the WP framework.

    I deeply appreciate your effort.

    May the force be with you.

  • Ginocolada says:

    Thanks for this very nice WordPress 3.x valid code.

    Would it be possible to do the above, but only for posts on a page? I’m want to create a page or category/archive to display my posts alphabetically instead of pages.

    I guess I have to use $posts instead op $paged?

  • RHO says:

    Thank you very much! That snipped saved me alot of time! Great!

  • Josh says:

    Thanks a million, Mac! One last question, if you don’t mind: how would I keep the list from paginating into higher letters and lower letters, but just appear as one large list?

  • Josh says:

    Would it be possible to do the above, but only for the sub-pages on a page? Or at the very list to exclude other pages?

    • Mac says:

      You just need to grab the ID of the Page before you do the query, and add the post_parent argument to the query:

      $id = $posts[0]->ID;
      $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
      $args = array (
      'post_parent' => $id,
      'posts_per_page' => $posts_per_page,
      'post_type' => 'page',
      'orderby' => 'title',
      'order' => 'ASC',
      'paged' => $paged
      );
      query_posts($args);

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>