3 Steps to Create AJAX Post Filters
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. 
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 withadmin_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:

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

Misha Rudrastyh
Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Whether this method is effective in a multi network plugin?
Hello,
yes, you can use this method with my Multisite plugin.
good work
thx
Use the wp-navi, I want a page feed.
Could you?
Hi,
yes, you can use wp-navi or the custom function from this tutorial about multisite pagination.
Awesome and helpful article. Thanks, Misha!
Is it possible to display on the same screen?
Of course — that’s how it should work.
the result is displayed id admin-ajax.php and not in the same page.
do you have any idea why?
Please check your JavaScript console (in browser) for errors.
Iv’e checked… on errors.
Any ideas?
If the result is displaying in admin-ajax.php it means only 1 thing – jQuery AJAX code doesn’t work.
Please contact me via email, I will help you.
Good read!
No need for event.preventDefault();?
$(‘#filter’).submit(function(event){
event.preventDefault();
no need, there is
return false
already.Is it possible to display all posts at first then apply a filter to those posts? I’m able to manipulate the WP_Query to display what I need, but only after I hit the “Apply Filter” button.
Thanks in advanced.
Hello Scott,
yes, just display all your posts here:
Very cool. Thanks.
Great write up, works like a charm.
How would add a second select for a second Taxonomy?
Just the same like the first, duplicate the necessary lines from each part of the code but to not forget to change taxonomy name.
Hello! Is it possible to increase the amount of posts that return after we apply the category filter. At the moment it seems to default to 10. Maybe an ajax “load more” button would work?
Hi John,
first option is creating load more button — I can not describe in two words how to do it here — maybe I will publish a post later about that.
Second option is to change default WordPress parameter in «Settings > Reading» from 10 to another value.
And the third option is to force
posts_per_page
parameter in the code in step 3 on line 2 ($args
array).Great pluggin Misha,
I would like to implement pagination or load more button. Pls can you point me out?
Kind Regards Andrija
Hello,
Yes, I recommend you to look at this post.
Thanks for quick replay. I have checked it bit dont know how to combine it with this post and already filtered data. Your tuts about filtering works perfect but cant figure this missing puzzle
You’re not the first who are asking about that, maybe I will write a separate detailed tutorial soon.
But the answer is too long to leave it in comments, sorry. Just check blog updates from time to time.
Hello everyone,
The new post about Filters + Load More is ready. I hope it will be helpful.
Hi Misha,
Thank you for this awesome tutorial. Working like a charm.
I want to show the excerpt. When using get_the_excerpt, it is outputting my shorcode tags.
On my normal pages the excerpt is working like it should, but i can’t get to work with your code.
Can you help me out.
Thanks
Hi, Rob,
thank you :)
Did you try this
$query->post->post_excerpt
?Yes! It is working.
Thank you so much.
Last question:
Is it posible to use this for multiple posttypes?
Do i have to create another hook?
Welcome!
Try to add the
post_type
parameter in the$args
array:Yes ! Thanks :)
I’ve tried this one before, but it’s showing all the posttypes together.
I have 3 pages, each with there own posttypes, so i want to filter within each one and just show the results separately on the pages.
Is this possible?
Ok, I understand,
First, depending on your page, add to the filter HTML the following:
After that in the PHP
$args
param add:Thanks Misha! It worked!
Hi Misha,
Thank you for this awesome tutorial.
I try this code separately with price and date its work well. Now i’m trying to merge it but fail. Can you please help??
Hi Farooq,
the line #3 of your code is wrong. Try to use ‘ASC’ or ‘DESC’ strings instead.
Hi Misha,
I try my best but unable to solve it. In your filter ASC & DESC for date is working,
Now i’m trying ASC & DESC for price custom field also.
Can you please elaborate it??
Best Regards.
Hi,
when you sort by meta values, meta key parameter is also required.
Could you please update this with ability to have pagination and also ‘infinite loading’? I’ll be very thankful!
Hi,
of course I will do it when I have time.
Hi, thanks a lot for making this tutorial! A quick question, in part 3 starting from line 60, is it possible to somehow hook an existing template without re-creating all the code? For instance, I have a custom archive page that already presents the posts exactly how I want them to look. Is it possible to use this archive to show the filtered results? Not sure if I’m explaining this right :)
Hi :)
I this case I usually use a custom function where I pass the $post object.
Hello Misha, nice work, works good! But would it also be possible to disable the apply button, and still filter the items the user has selected?
Hello,
yes, it is possible, but now I’m in the mountains and have only iPhone – so I can not provide you with the code, sorry.
No problem, i’ll wait until you have got the tim,e when you get back. Enjoy your vacation?
Ok, thank you! I will come back on 7th February.
I am wondering how hard is it to get the ajax to run on a change of the select dropdown. I have tried this using
Hi Daniel,
I recommend you to implement it the following way:
The second step is jQuery:
As someone who was struggling with this, I want to mention that I had to make an additional change for this to work:
From this:
To this:
great stuff :)
Thank you :) I will add it to the post.
Great tutorial mate, I’ve got only one problem:
– dropdowna list is showing categories, but ajax response return only data from post_type =>post,
I’ve set the post type to my custom post type – portfolio :
But it’s still not working. Can you please help me?
Hi Maciek,
thanks,
the post_type parameter should never be inside tax_query. Add id to the
$args
array the following way:Awesome stuff :) Cheers! :)
One more question – how to submit form on clicking radio button?
Try to replace this part of the jQuery script:
to this:
Ok thanks :) Last one thing:) Is there a way of showing in list of categories something like ‘All’? So when I click on it it’s gonna show all categories?
Just add to your select dropdown something like:
Dear Misha
Thanks for this amazing tuto
I’ve tried to add this : All categories to show all results
But it doesn’t work, it shows : nothing found.
Do you know why ?
Other thing
I’d like to use the filter twice in the same page cause i have two tab with different posts i’d like to filter by ‘city terms’
But, in the second tab, it doesn’t work with ajax but it works if i click on the button
I’ve tried for hours but i can’t do…
Could you help me
Even if i understand you made a lot
Many thanks
Damien
About all categories – there is somewhere a small mistake in your code, please look.
About two filters. As an option use different IDs in HTML elements, use twice the given jQuery code with the different selectors and AJAX action parameters.
It depends on your situation of course, but PHP code can be the same – in that case AJAX action parameter will be the same.
Hi Misha,
I use this, for my home page, and Anytime the result is “0”…
Can you help me ?
Thank you so mush :3
Hi Beifong,
it means that ajax is ok, but it is not connected to the hook/function. Please read the post one more time and pay attention to the
action
hidden field and hook ( add_action() arguments ) names.Works perfect. I have been using this for many times
How can I trigger some jQuery after ajax is done loading?
Thanks!
You can add in into the
success:function(data){ }
(Step 2).This works for me. Thank you!
Hello, Ali.
I had the same problem with my custom post type category. Probably, you are already find the answer, but maybe some one else will need it
Thank you Tanya for your useful comment :)
this save my day !!!! thx you very much ;)
Hi Misha i tried to used the form but i have some questions, if i used checkboxes, (one checkbox for one taxonomy for example), when i select one check box the result is ok, but when i check two checkboxes (two taxonomies terms) , the resul just show the post that belog to one term??
Hi Javier,
did you use the same name attributes for both checkboxes?
Hi finally i can use the checkbox for two taxonmies (marca and edad) but now my problem is that i want display only post for a especific categories, (i working with woocommerce) my code is:
Where are your specific categories in your code?
I do not like the way how you filter taxonomies, please look, how
meta_query
implemented in the post, and make the same fortax_query
– they are very similar.P.S. Please, use the buttons in editor (php, js etc) when want to insert the code in comment!
Hi Misha, the code only show 5 of 30 results, How can I do to show all the results?
Hi Mark,
try to add
posts_per_page
parameter to your$args
array.Thanks Misha! I was able to do something very similar and it worked perfectly following your tutorial.
If you have an archive of Movies and the user selects 3 filter options (Horror, Comedy, Documentary), how would they be able to send that page with those filters selected? How would you generate a shareable link?
Looking forward to your response!
Thanks,
Jose
Hi Jose,
actually there are two ways – you can use $_GET parameters in URL or a hash.
So,
?post_tp=Horror,Comedy,Documentary
or#post_type=Horror,Comedy,Documentary
.When the filter page is loading, you’re checking the
$_GET['post_tp']
, select the appropriate values in the form and run your custom WP_Query for those parameters.Second way is when you check the parameters in JavaScript
window.location.hash
, after that you select the appropriate values in the form and script send the AJAX request.P.S. I do not recommend to use
post_type
as a $_GET parameter name, because there are could be conflicts.Hi Misha, I really appreciate the code.
Could you please let me know where the PHP code from step 3 should be placed?
Currently I have:
Step 1 (form): In the relevant page template.
Step 2 (jQuery): In a custom plugin, consisting of a PHP file and a JS file. PHP is shown below.
Step 3 (PHP): I currently have it placed as an ‘include’ in the php of the custom plugin. However I wonder if this is what you would suggest?
– I tried putting it directly in the page template but it didn’t work.
Right now my custom plugin php file looks like this…
I very sincerely appreciate your advice.
Hi Ben,
Try to use the Step 3 code in the
functions.php
of your current theme.default action return from admin-ajax.php
Hi Misha,
Awesome filter plug-in. It is doing exactly as I need.
Is there a way to reset the filter, without reloading the page?
Regards,
Rafael
Hi Rafael,
Well, ok, the first step is creating a button or link element with the ID #reset for example.
Second step is jQuery code, something like this:
Hello Misha,
Thank you, but sorry I wasn’t fully clear on my question earlier.
I have used this tutorial now to filter posts based on categories in WordPress.
At first it displays all the posts from all categories and the filter then displays posts only from a certain category.
Is it also possible to refresh / reload the filter so all posts from all categories are displayed again, and the filter is set back to the default selection?
Thank you