3 Steps to Create AJAX Post Filters

In this tutorial I will show you how to create an AJAX content filter for posts or pages, or custom post types with the ability to sort by categories (taxonomies) or by custom field values and order posts by date – ascending or descending.

#admin-ajax.php  /    /   269

So many people asks me about post filters. So I decided to write a simple post about it — so everyone could understand how it works.&nbsp

In this post I will show you how to create an asynchronous AJAX filter by yourself, which allows to filter posts by taxonomy terms, meta values and sort results by publish date.

I do not recommend to use plugins for these purposes if possible.

Step 1. Everything begins with a HTML form #

Our filter form will consist of 4 parts. I’ll describe each part of the form separately.

1.1 Filter posts by a category or by taxonomy terms #

First part of the form is a dropdown <select> of taxonomies. To create that <select> you can freely use get_terms() function. This function works not only for custom taxonomies but for default categories and tags as well.

if( $terms = get_terms( array(
    'taxonomy' => 'category', // to make it simple I use default categories
    'orderby' => 'name'
) ) ) : 
	// if categories exist, display the dropdown
	echo '<select name="categoryfilter"><option value="">Select category...</option>';
	foreach ( $terms as $term ) :
		echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as an option value
	endforeach;
	echo '</select>';
endif;

You can replace the taxonomy parameter value on line 2 with any custom taxonomy name or post_tag.

By the way, it is also possible to combine this code with my multisite plugin, you get it here and network_get_terms() function. So, all network categories will be in the select dropdown and all the network posts will be displayed as the filter search results.

1.2 Filter posts by custom field values as well #

I use range of prices. In our case price is a custom field value that is stored in wp_postmeta table under the _price key in database.

<input type="text" name="price_min" placeholder="Min price" />
<input type="text" name="price_max" placeholder="Max price" />

I will show you how to create a meta_query filter in step 3 of this tutorial but you can also read more about meta_query usage here.

1.3 Ascending or Descending Order #

Simple radio buttons will help us to sort posts in ascending or descending order.

<label>
	<input type="radio" name="date" value="ASC" /> Date: Ascending
</label>
<label>
	<input type="radio" name="date" value="DESC" selected="selected" /> Date: Descending
</label>

For simpleness I decided to sort posts by date only, but you can easily sort posts by name alphabetically or by custom fields values.

1.4 Checkbox filter – Display posts with featured images only #

Actually the featured image is just an attachment ID that is stored like a custom field value under _thumbnail_id key. We will just check if it exists.

<label>
	<input type="checkbox" name="featured_image" /> Only posts with featured images
</label>

You can also add a search field just like a simple <input type="text" /> and in Step 3 use s= parameter of WP_Query. Easy-peasy 😁

Complete form code #

You can skip all the previous field descriptions and use the code below as is. Insert it anywhere you want the filter to be.

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">
	<?php
		if( $terms = get_terms( array( 'taxonomy' => 'category', 'orderby' => 'name' ) ) ) : 
 
			echo '<select name="categoryfilter"><option value="">Select category...</option>';
			foreach ( $terms as $term ) :
				echo '<option value="' . $term->term_id . '">' . $term->name . '</option>'; // ID of the category as the value of an option
			endforeach;
			echo '</select>';
		endif;
	?>
	<input type="text" name="price_min" placeholder="Min price" />
	<input type="text" name="price_max" placeholder="Max price" />
	<label>
		<input type="radio" name="date" value="ASC" /> Date: Ascending
	</label>
	<label>
		<input type="radio" name="date" value="DESC" selected="selected" /> Date: Descending
	</label>
	<label>
		<input type="checkbox" name="featured_image" /> Only posts with featured images
	</label>
	<button>Apply filter</button>
	<input type="hidden" name="action" value="myfilter">
</form>
<div id="response"></div>

Some comments:

  • Line #1. I use default WordPress function site_url() to get an actual website URL
  • Line #1. admin-ajax.php is the default WordPress AJAX processor. I place it into the form action attribute just for simplicity, you can also get it with admin_url('admin-ajax.php').
  • Line #23. Hidden input field with the myfilter attribute is required — this is how WordPress recognize what function to use.
  • Line #25. #response div element is the container where the code will paste the result data.

Step 2. jQuery script to Send a Request and to Receive Result Data #

In this part of the tutorial I suppose that you know just a little bit about jQuery, at least how to include it to a website page. Here is the complete jQuery-based processing code. It will send the request when the form is submitted.

jQuery(function($){
	$('#filter').submit(function(){
		var filter = $('#filter');
		$.ajax({
			url:filter.attr('action'),
			data:filter.serialize(), // form data
			type:filter.attr('method'), // POST
			beforeSend:function(xhr){
				filter.find('button').text('Processing...'); // changing the button label
			},
			success:function(data){
				filter.find('button').text('Apply filter'); // changing the button label back
				$('#response').html(data); // insert data
			}
		});
		return false;
	});
});

Step 3. PHP code to Process the Request #

I think it is the most interesting part. In this part you decide how to filter the posts the best way. This code is fully based on WP_Query. But it is also a good idea to do it with query_posts().

add_action('wp_ajax_myfilter', 'misha_filter_function'); // wp_ajax_{ACTION HERE} 
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');
 
function misha_filter_function(){
	$args = array(
		'orderby' => 'date', // we will sort posts by date
		'order'	=> $_POST['date'] // ASC or DESC
	);
 
	// for taxonomies / categories
	if( isset( $_POST['categoryfilter'] ) )
		$args['tax_query'] = array(
			array(
				'taxonomy' => 'category',
				'field' => 'id',
				'terms' => $_POST['categoryfilter']
			)
		);
 
	// create $args['meta_query'] array if one of the following fields is filled
	if( isset( $_POST['price_min'] ) && $_POST['price_min'] || isset( $_POST['price_max'] ) && $_POST['price_max'] || isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
		$args['meta_query'] = array( 'relation'=>'AND' ); // AND means that all conditions of meta_query should be true
 
	// if both minimum price and maximum price are specified we will use BETWEEN comparison
	if( isset( $_POST['price_min'] ) && $_POST['price_min'] && isset( $_POST['price_max'] ) && $_POST['price_max'] ) {
		$args['meta_query'][] = array(
			'key' => '_price',
			'value' => array( $_POST['price_min'], $_POST['price_max'] ),
			'type' => 'numeric',
			'compare' => 'between'
		);
	} else {
		// if only min price is set
		if( isset( $_POST['price_min'] ) && $_POST['price_min'] )
			$args['meta_query'][] = array(
				'key' => '_price',
				'value' => $_POST['price_min'],
				'type' => 'numeric',
				'compare' => '>'
			);
 
		// if only max price is set
		if( isset( $_POST['price_max'] ) && $_POST['price_max'] )
			$args['meta_query'][] = array(
				'key' => '_price',
				'value' => $_POST['price_max'],
				'type' => 'numeric',
				'compare' => '<'
			);
	}
 
 
	// if post thumbnail is set
	if( isset( $_POST['featured_image'] ) && $_POST['featured_image'] == 'on' )
		$args['meta_query'][] = array(
			'key' => '_thumbnail_id',
			'compare' => 'EXISTS'
		);
	// if you want to use multiple checkboxed, just duplicate the above 5 lines for each checkbox
 
	$query = new WP_Query( $args );
 
	if( $query->have_posts() ) :
		while( $query->have_posts() ): $query->the_post();
			echo '<h2>' . $query->post->post_title . '</h2>';
		endwhile;
		wp_reset_postdata();
	else :
		echo 'No posts found';
	endif;
 
	die();
}

Now you know the basics and you can easily create filters like this in WordPress:

Product filter example from eBay

If you still have problems with filters, please leave a comment below.

Misha Rudrastyh
About the author Misha Rudrastyh

Passionate about WordPress and snowboarding, creating websites for over 10 years! Let's work together — just contact me.

If you are a developer too, subscribe to my facebook page.

Comments 269

← Older
  • Hello, thank you for your post! I am able to get the filter working but the results go to the url /wp-admin/admin-ajax.php

    Shouldn’t the results be posted in the on the same page as the filter?

    • Hello,

      Usually it happens because of a JavaScript error. Please check your browser console.

      • I was able to get this working. Thank you again for your post!

        One thing though. Do you happen to know a way or can you point me to some documentation to add a search to this sort / filter form?

        • It is all about WP_Query orderby parameters. You can find it in official WordPress Codex.

  • Very nice, thank you!

    I was wondering if you could help me, I want the date filter (ASC & DESC)to be in a “Select”.
    Only the Desc is working, JS doesn’t show error and the posts load.

    <select>
         <option name="date" value="ASC">Date: Ascending</option>
         <option name="date" value="DESC" selected="selected">Date: Descending</option>
    </select>

    ps.: Sorry for my English

    • Oh and I use the code you provided*

    • MishaAuthor

      Hey Samuel,

      <select name="date">
           <option value="ASC">Date: Ascending</option>
           <option value="DESC" selected="selected">Date: Descending</option>
      </select>
      • Thanks alot Misha!Damn how didn’t I see that !

        I was wondering if you had other blogs/articles about this topic, I am new to wordpress development and I actually didn’t find alot of usefull guides. (Yours is the best!)

        Im trying to filter my posts from 1 category by: Data, Title, Alphabetically and by popularity

        Thanks again ! Have a good day !

        • MishaAuthor

          Just this one

          Thank you so much πŸ™ƒ I wish you an awesome day too!

          • Hey Misha, I was unable to get a dropdown to sort by title with your code to sort by date. Any chance you could help or show me the direction?

            Thanks (a great posts) !
            Have a good day.

          • Finally I got it working if anybody need it.

            <form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">
                           <select name="filterBy">
                              <option value="date" selected="selected">Date</option>
                              <option value="title">Title</option>
                           </select>
                           <select name="order">
                              <option value="ASC">Ascending</option>
                              <option value="DESC" selected="selected">Descending</option>
                           </select>
                           <button>Apply filter</button>
                           <input type="hidden" name="action" value="myfilter">
                        </form>
                        <div id="response"></div>
            <?php
            add_action('wp_enqueue_scripts', 'my_theme_enqueue_styles', 11);
            function my_theme_enqueue_styles() {
                wp_enqueue_style('child-style', get_stylesheet_uri());
            }
            ?>
             
            <?php
            function misha_filter_function() {
                //print_r($_POST);
                $args = array('orderby' => $_POST['filterBy'], // we will sort posts by date
                'cat' => '4', 'order' => $_POST['order'] // ASC ΠΈΠ»ΠΈ DESC
                );
                // for taxonomies / categories
                if (isset($_POST['categoryfilter'])) $args = array('post_type' => 'post', 'tax_query' => array(array('taxonomy' => 'people', 'field' => 'slug', 'terms' => array('news-events'),),),);
                $query = new WP_Query($args);
                // if post thumbnail is set
                if (isset($_POST['featured_image']) && $_POST['featured_image'] == 'on') $args['meta_query'][] = array('key' => '_thumbnail_id', 'compare' => 'EXISTS');
                $query = new WP_Query($args);
                if ($query->have_posts()):
                    while ($query->have_posts()):
                        $query->the_post();
                        echo '<h2>' . $query->post->post_title . '</h2>';
                    endwhile;
                    wp_reset_postdata();
                else:
                    echo 'No posts found';
                endif;
                die();
            }
            add_action('wp_ajax_myfilter', 'misha_filter_function');
            add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');
            ?>
  • Hey Misha !

    I am trying to add a popularity filter… Cant get my head around how to implement it with your code.

    Any chance you could help? Or give a a direction.

    Thanks and have a good day.

    • MishaAuthor

      Hey Sam,

      How would you like to measure popularity? By views?

      • Hey Misha, good morning !
        Yes, I think it is the easiest way?
        (Now I have a dropdown to filter by Date Or Title and another dropdown to filter by ASC or DESC)

        Thanks !

      • MishaAuthor

        Hi Sam,

        If you would like me to review your code or to help you with the code, directly on your website, please contact me by email.

        Here in comments I can help you with an advice or some code snippets.

        If you would like to filter posts by popularity, I think the best way is to use post meta with views, let’s say it will be views meta key.

        So, here are could be two steps:

        1. Add a dropdown into the form, like this

        <select name="by_popularity">
        <option value="">Filter by popularity</option>
        <option value="DESC">Most popular first</option>
        <option value="ASC">Less popular first</option>
        </select>

        2. In the step with PHP you can add the filtering like this:

        // define your params for WP_Query/query_posts first
        $args = array(
         
        	...
         
        );
         
        // if the filter is selected let's sort by it.
        if( !empty( $_POST['by_popularity'] ) && $_POST['by_popularity'] ) {
         
        	$args['meta_key'] = 'views';
        	$args['orderby'] = 'meta_value_num';
        	$args['order'] = $_POST['by_popularity'];
         
        } 
         
        query_posts( $args );

        I see there are two dropdown selects on your website, but the algorithm is the same πŸ™ƒ

  • hi man thanks for you blog.

    How can I filter taxonomy from another taxonomy for example:

    Select 1: Countries: USA, Canada, Mexico
    Select 2: Load states form selected country.

    Thank you so much

    • MishaAuthor

      Hi Juan,

      Well, here is the detailed explanation. You mentioned something about another taxonomy – No, it must be the same hierarchical taxonomy, but states are child items of countries.

      Step 1. jQuery event

      When someone selects a country, the code will send AJAX request to receive its states.

      jQuery(function($){
       
      	$('#country').change(function(){
      		var country_id = $(this).val();
      		if( country_id ) {
      			$.ajax({
      				type : 'POST',
      				url : 'http://your-website/wp-admin/admin-ajax.php',
      				data : 'action=getstate&country_id=' + country_id,
      				beforeSend: function (xhr) {
      					$('#state').html('<option value="">Loading...</option>');
      				},
      				success: function( data ){
      					$('#state').html(data);
      				}
      			});
      		} else {
      			$('#state').html('<option value="">All</option>');
      		}
      	});
       
      });

      Step 2. Process AJAX request in PHP

      This code is for your functions.php file.

      add_action('wp_ajax_getstate', 'misha_load_child_state'); // wp_ajax_{action parameter}
      add_action('wp_ajax_getstate', 'misha_load_child_state');
       
      function misha_load_child_state(){
       
      	echo '<option value="">All</option>';
       
      	if( !empty( $_POST['country_id'] ) ) {
       
      		if( $locations = get_terms( array(
      			'taxonomy' => 'location', // change your taxonomy name here!
      			'parent' => $_POST['country_id'],
      			'orderby' => 'name',
      			'order' => 'ASC'
      		) ) ) {
      			foreach( $locations as $location ) :
      				echo '<option value="' . $location->term_id . '">' . $location->name . '</option>';
      			endforeach;
      			die();
      		}
       
      	}
      	die();
       
      }
      • Thanks for you help but I think I don’t explain the correct way.

        I have two taxonomies: 1. Languages 2. Countries.

        I’m triying do a filter post_type. When I create a post_type I can categorized it with language and country.

        When the people choose a languages in the first select the second select will update and show the countries that have posts with the language choosed.

        Thanks for your help.

        • MishaAuthor

          Then change my Step 2 and write a function which will return countries depending on a selected language. Not sure how you will do it, to loop through all the post types doesn’t seem to me as a good idea. But if there is not so much posts, you can do it.

          You can also consider using a single taxonomy.

Comments are closed.
If you need my personal help, please contact me. Contact me