Create an A-Z Filter For WordPress Posts

You’ve most likely seen these A-Z filter areas on your travels. They’re pretty useful if have a site that contains a lot of index type posts, such as users in a membership site, a book review site, or a movie blog, etc and you would like to offer users a simple way to filter through this content by displaying an alphabet menu that they can filter by letter or number.

Demo can be found here.

Create a template file in WordPress

Let’s create a template page called A-Z, upload to your website and attach this a page.

<?php /* Template name: A-Z */
get_header(); ?>
       
   <div id="main-area">
      <div class="wrapper">
         <div <?php post_class(); ?>>
	        // All of our code will go here				
         </div>			
      </div>
   </div>
    
<?php get_footer(); ?>

Return all posts and get first letter from each post

We are going to create a custom loop to pull in all of the posts we want to show. You will want to tweak your options to suit your needs but In my case, I want posts, in the tutorial category, and the posts_per_page -1 option will display every post.

$filter_args = array
(
   'post_type' => 'post',
   'posts_per_page' => -1,
   'category_name' => 'tutorial',
);

Next, we will create a new WP_Query object called $tutorial_posts with our $filter_args array options. after that, we will create an empty array called $posts_array to store the first letter of our posts.

$tutorial_posts = new WP_Query($filter_args);
$posts_array = array();

Now, we just need to run our custom loop with our $tutorial_posts object to grab all posts, convert each post to lower case, and get the first letter from each post.

while ( $tutorial_posts->have_posts() ) : $tutorial_posts->the_post(); 
   $posts_array[] = strtolower(get_the_title()[0]);
endwhile; ?>

After this loop has finished, the $posts_array will hold all of the first characters of our posts. if we were to var_dump this array, we would see the following:

array(14) {
  [0]=>
  string(1) "e"

  ...

  [13]=>
  string(1) "r"
}

Building our A-Z filter list

Now we have all of the first characters that are available to us – we can start to build the A-Z filter.
First, we are going to generate the list of A-Z characters (with the # symbol before the loop for numbers).

<?php 
$alphabet_array = range('a', 'z'); // Generate our A-Z list of characters
?>

<ul id="a-z">
<li>#</li> // Include our # for numbers
<?php foreach ($alphabet_array as $letter) // Loop through the alphabet and spit out a list item with the letter
{
   echo "<li>" . $letter . '</li>';
}
?>
</ul>

After running this code, we are left with an unformatted list. We just need to style it.

Styling the list

#a-z {
	float: left;
    width: 100%;
	margin-bottom: 25px;
	display: flex;
	flex-direction: row;
}
	ul#a-z li {
		flex-grow: 1;
		padding: 7px;
		text-align: center;
		background: #dadbdc;
		color: #fff;
		text-transform: uppercase;
		border-left: 2px solid #fff;
	}

Now we are left with a nicely formatted list like below.

Enabling characters in our list

So, we have a nice looking a-z list, but we can’t tell what characters are available to click and interact with. We need to fix that.

Checking if any posts begin with a number

The first thing we do before the foreach loop is run is to check if any posts begin with a number, so we are going to create a $number_array.

$alphabet_array = range('a', 'z'); 
$number_array = range(0, 9); // New array created (0-9)

And then using PHP’s array_intersect function, we can check if any characters from the $posts_array have numbers from the numbers array in it. If posts begin with a number, we will apply a class called active to the list item, if not, we will just display the list item.

From here I will also start adding a custom data attribute named data-letter to our list items to be used later on when we want to filter the posts.

if(count(array_intersect($posts_array, $number_array)) > 0)
{
   echo "<li class=\"active\" data-letter=\"#\">#</li>";
}
else 
{
   echo "<li data-letter=\"#\">#</li>";
}

// foreach loop

If any of our posts begin with a number, the active class will be added.

Enabling available letters

Now we need to go back to our foreach loop and check if any of our post’s first letters are in our alphabet array.

foreach ($alphabet_array as $letter)
{
   if (in_array($letter, $posts_array)) 
   {	
      echo "<li class=\"active\" data letter=".$letter.">$letter</li>"
   }
   else 
   {
      echo "<li data-letter=".$letter.">$letter</li>";
   }
} 

The A-Z list will now include available list items with the active class. Let’s create an .active class in our CSS to make this darker than the rest.

ul#a-z li.active {
   background: #9c9c9c;
   cursor: pointer;	
}

We now have a nifty A-Z list! Up next, we are going to build our index of posts area underneath.

Building our posts index

We are going to reuse our previous $tutorial_posts object to load all of our posts within our next WordPress loop. Explanation of what is going below.

<?php 
$i = -1; // We create a counter and give it a value of -1 as we will use this counter 
         // with the $posts_array and array's indexes begin at 0, so this 
         // just syncs everything up.
?>
				
<ul id="posts-results">
<?php while ( $tutorial_posts->have_posts() ) : $tutorial_posts->the_post(); // Loop through all posts
   $i++; // Increment counter by 1 on each loop iteration
				
   if(is_numeric($posts_array[$i])) // Check if any of the posts are numeric
   {
      echo "<li data-letter=\"#\">"; // If number, add # to data-letter attribute
         echo "<a href=".get_the_permalink().">";
            echo get_the_title();
         echo "</a>";
      echo "</li>";
   }
   else // If posts begin with letter
   {
      echo "<li data-letter=".$posts_array[$i].">"; // Add letter to each data-letter attribute
         echo "<a href=".get_the_permalink().">";
            echo get_the_title();
         echo "</a>";
      echo "</li>";
   } ?>			
<?php endwhile; ?> // End while loop
</ul>

What’s returned now is all of our posts, unformatted. We just need to style posts these with the below CSS.

#posts-results a {
   border-bottom: 1px solid #ccc;
   display: block;
   padding: 10px 0;
}

So we are left with this.

Using JavaScript to add the filtering functionality

The way we are going to filter our posts is first hiding all posts and using a show class to display them. So before we create our JavaScript, let’s add some CSS.

#posts-results li { /* By default, hide all posts */
   display: none; 
}
#posts-results li.show { /* When a show class is used, this displays posts */
   display: block; 
}
ul#a-z li.current { /* This class will be used to highlight the current A-Z list item */
   background: #de466c;
}

We can now create our jQuery code to show the first few posts, depending on what character is available and then highlight the correct A-Z character.

jQuery(document).ready(function()
{	
   "use strict";
	
   let initial_first_letter = jQuery('#a-z li.active:eq(0)').data('letter'); // Create var to get the data-letter of the first active list item
   let posts_results_li = jQuery("#posts-results li"); // Create var for all post result list items
	
   // Initial load of page
   jQuery("#posts-results li[data-letter="+initial_first_letter+"]").addClass('show'); // Show post results with first available active class added
   jQuery('#a-z li.active:eq(0)').addClass('current'); // Add the current class to the first item that has the active class
});

On initial load of the page we can see that the first character available is a number so the # in our A-Z list is highlighted, and secondly all posts are hidden and only posts that begin with a number will display. If no, posts began with a number, the next available character would be highlighted and shown.

Make the A-Z list interactive

The final thing we need to do is to allow our users to interact with the A-Z menu and show posts from different characters.

let click_first_char; // Create var to hold what character is clicked

// Clicking A-Z list items
jQuery('#a-z li.active').click(function() // User clicks A-Z list item
{
   jQuery(posts_results_li).removeClass('show'); // Hide all posts
   jQuery('#a-z li.active').removeClass('current'); // Remove current class from all A-Z list items
   click_first_char = jQuery(this).data('letter'); // Add clicked character to click_first_char variable
   jQuery(this).addClass('current'); // Add current class to clicked A-Z list item
   jQuery("#posts-results li[data-letter="+click_first_char+"]").addClass('show'); // Show posts that match clicked character
});

Last few bits and wrap up

The very last thing I would like to add here are two features to make this nice and user friendly:

  1. A show all ‘link’
  2. A title showing the current character in use

After our A-Z list markup, I have created an area called title-status. This will hold our two features.

<div id="title-status">
   <p>Showing: <span></span></p>
   <p id="show-all">Show all posts</p>
</div>

Now we style this area with some CSS

#title-status {
   float: left;
   width: 100%;
}
   #title-status p {
   float: left;
   width: 50%;
   margin-bottom: 15px;
   font-size: 16px;
}
   #title-status span {
      font-weight: bold;
      text-transform: uppercase;
   }
#title-status p:last-child {
   text-align: right;
   text-decoration: underline;
   color: #de466c;
   cursor: pointer;
}

And we have this.

The very last piece of the puzzle is adding a few more lines of JavaScript.

// Let's create 3 new variables
let title_showing = jQuery('#title-status span'); // Create new var to hold what character we are viewing
let az_li = jQuery('#a-z li'); // Create new var to get all A-Z list items 
let title_show_all = jQuery('p#show-all'); // Create new var to target the show-all paragraph tag

// Initial load
title_showing.text(initial_first_letter);

// Clicking A-Z list items
jQuery('#a-z li.active').click(function()
{
   ...

   title_showing.text(click_first_char);
   title_show_all.show();
});

title_show_all.click(function() // Show all posts function
{
   posts_results_li.addClass('show'); // Show all posts
   title_showing.text('all'); // Append the word all to our span inside the #title-status paragraph
   az_li.removeClass('current'); // Remove current class from all A-Z list items
   jQuery(this).hide(); // Hide title_show_all link
}); 

We now have a nifty interactive filter working now! Have fun with it. Here’s a link to Github to download all of the code used.

You can reply to this post by tweeting to me