Filter WordPress posts by multiple taxonomy terms with AJAX

UPDATE: 14. Nov. 2016
Demo is now extended to show standard numbered pagination or infinite scroll.

This is the second part of the post where I show how to display and filter WordPress posts based on taxonomy term.
In this post I will create a working script which shows how to filter posts by multiple terms.

I will include the entire code but I will not explain in depth what’s going on there because I already did that in previous post.
If you missed the first part please check here How to filter WordPress posts by custom taxonomy term with AJAX and pagination.


The idea is very simple, in the previous post you can find out how to:

  1. Display list of terms
  2. Get taxonomy and term of clicked element
  3. Get and display posts by selected term

This post will just extend this feature so instead only one element, we will get all active terms and retrieve posts for all active terms.
To do that we would just need to slightly modify our javascript to:

What does this do?

  1. On click toggle parent li item class ‘active’
  2. Get all .active li items
  3. Get ‘taxonomy’ and ‘term’ slug from all elements: $terms[taxonomy_name] = ['term1', 'term2'];
  4. Pass params to our function that gets posts by selected terms

Function which gets posts by AJAX stays the same like in previous posts.

PHP function

After we get all params with jQuery we need to get posts by selected terms. To do that we need to modify our PHP function, and use ‘tax_query’ in our WP_Query.
Snippet from function:

What does this do?

Basically script stays the same like in previous post, the only difference is that we now pass data as an array of 'taxonomy' => array_of_terms[]' and we need to loop trough this array and construct ‘tax_query’.

Working demo example with pagination
Working demo example with infinite scroll


  1. PHP file Gist
  2. Javascript file Gist


    1. Hi Rubic,

      Thanks for reporting, I have fixed the issue.
      Problem was that I forgot to include ‘wp_ajax_nopriv’ so it was not working for logged out users.


  1. Hi, thank you for tutorial, when i first try your demo, its doesn’t work, but now its fixed for logged out users :)
    I wonder how to show all posts when:
    – User visit page with all posts (blog in my case) for the first time
    – User first select some terms, and after that un-select everything (return to default state)
    Also what do you think about using/adding hidden term list item, that will contain default wordpress category?
    I mean: my default wp category is Uncategorized(for testing this) and i have cat 1 to 12 as children of uncategorized
    When i make hidden list item li.uncategorized .active
    it should load all posts by default, whnever i click on other item it should remove active class from li.uncategorized .active, so my sorting should work??
    Currently i’m using barba.js pjax, so my testing website feels like its fully ajaxified, i read somwhere that pjax is better for seo than ajax, its doesn’t hide content for search engines
    But if we could load all posts by default, or load all posts when any terms aren’t selected it still should be fine for seo, yes?

  2. Hi Gabrielle,

    I’ve updated example to include ‘Show all’ which is selected by default.
    Hope this clears all your questions.

  3. Hi Vlado,

    Wonderful guide, works perfectly for me!

    Next step for me is to create infinite-scroll / load more pagination instead of page numbering to make it all ajaxified :).

    Anyone that has managed to do this?

  4. Would be amazing, I don’t think it should be a huge deal while you’re already using a sort of ajax-pagination: I just think the load more option is nicer than replacing a number of posts with new posts (page numbers).

    Meanwhile I will try to expand the filtering of posts by adding filters from meta data in select inputs. Cause im using your function with custom taxonomies to filter apartments by “Place”. Now i need to add “No of rooms” and “Min/Max-price” in select dropdowns. I guess by adding some kind of meta_query key->rooms type of thing.


    1. Hi,
      Article is now updated to work either with infinite scroll or numbered pagination.
      Please check the demo with infinite scroll here and update your code accordingly.
      I’m not sure are you using shortcode or not, but to use infinite scroll you would need to add param pager=”infscr”, that would display ‘Load more’ button instead of numbered pagination.

  5. Hi Vlado,

    I’m also intarasted in this infinite-scroll option.

    Could you help me with query for time.
    I would like to user can filter by X h ago for example …

    Thanks Nikola

    1. Article is now updated to work either with infinite scroll or numbered pagination.
      Please check the demo with infinite scroll here and update your code accordingly.
      I’m not sure are you using shortcode or not, but to use infinite scroll you would need to add param pager=”infscr”, that would display ‘Load more’ button instead of numbered pagination.

      Regarding the time query, please consult WP_Query documentation on codex, the best way is to trial and error until you get desired results on your localhost.

  6. Hi Vlado,

    could you please check your files on github.
    I really tried all but there are some differences between codes on post / github and your working code :) .

    First of all in any file i cannot find this piece of code for example:

    <a href="#" data-filter="taxonomy; ?>” data-term=”all-terms” data-page=”1″>
    Show All

    so i need to search inside post to see what changes you have made and that is not problem,
    problem is that doesn’t work after that for example on infinity scroll i get infinity scroll and i get paging, when i click load more it reload second page with posts, and if i use pager=”infscr” it just show me pagination :)

    If you can make resume with final files which you use on your site it would be very nice!

    I will really appreciate your help and thanks once again for this great tutorial!


  7. Hey Vlado Bosnjak,

    I am using tag with options for the filter selection. How can i make your code work with my markup?

    $(‘.sc-ajax-filter-multi’).on(‘click’, ‘a[data-filter], .pagination a’, function(event) {}

    I have tried replacing a[data-filter] with option[data-filter] doesn’t work. Console log returns nothing

    Please help!

    1. Hi Dylan,

      Sorry but I’m not sure I understand whats wrong.
      If you are using select, then you probably should do:

      $('select').change( function() { ... })

      To get the selected option

  8. Great tutorial, I learned a bunch here that directly applies to the project I’m putting together. Thank you!

    I have a question about setting the specific terms to be used with // ‘terms’ => false, // Get specific taxonomy terms only

    Is it best practice to use the term_id? I suppose it would be because if you changed the name then the id would still be maintained? If desired, would it be easy enough to change the list of terms to names or slugs?

    thanks again.

    1. Hi Stephen,

      Yes, I think by ID would be best.
      You can get entire term object and then display what you like.

  9. great ! I love this tutorial and codes.but could you please tell me how to show category name instead of category slug in the selection confirmation msg section? thank you very much

    1. Hi Eric,

      I’m not sure where exactly is this code, but if $trm = slug of the term, you can get term data with get_term_by function. Anyhow, you can do print_r and see what is $term.

  10. Hy Vlado,

    awesome posts – it pushes my concept in the right direction! But what if i have to filter in multiple taxonomies. In my case i have to filter a recipe database ( One of the taxonomies is “season”, another is “ingredients”. I have to combine them in a way like in this example ( Do you have a idea how to achieve that?

    Thanks for your reply and kind regards,

    1. Hi Bernhard,

      Basically it’s the same thing. Just instead of 1 tax add array.
      In the shortcode part here L8:

      $a = shortcode_atts( array(
              'tax'      => ['tax_1', 'tax_2], // Taxonomy
              'terms'    => false, // Get specific taxonomy terms only
              'active'   => false, // Set active term by ID
              'per_page' => 12 // How many posts per page,
              'pager'    => 'pager' // 'pager' to use numbered pagination || 'infscr' to use infinite scroll
      ), $atts );

      Then, on L16, you don’t do get_terms($a['tax']);, but instead you do:

      foreach ($a[tax] as $tax) {
         ... the rest of code ...

      That’s roughly, but in short you just need to convert my code for one taxonomy into for/foreach loop.
      On click of each term, you mark him active and when making request get all with class ‘active’ and read tax/term_id from them, and construct WP_Query out of it.


  11. This is a life-saver!!! I have more complex taxonomy terms, but at least this gets me started! I was never able to figure this out on my own and now I can ajaxify ALL the things! Thank you so much!!

  12. amazing tutorial just one question, how can i use 2 post type and create 2 shortcut of them for echo in different area’s like :
    echo do_shortcode('[ajax_filter_posts_moves per_page="10"]');
    that show post’s from moves post type and :
    echo do_shortcode('[ajax_filter_posts_series per_page="10"]');
    and this one show posts from series post type
    p.s:sorry for bad english hope you understand my question .

    1. really thanks for tour answer sir , can you explain more? duplicating shortcut is easy the problem is how i can tell the program if post_type = movies echo ajax_filter_posts_movies and next time put post_type = series and echo ajax_filter_posts_series there is a way or no way to do this?

    2. Purpose of this tutorial is to show you how things work so you can use the logic and create what you need for your self.
      It would be difficult for me to include all possible variations that would cover all available cases.

      What you need to do is to extend shortcode function to accept one more param ‘post_type’, and pass that param along with all others to the function.
      It really shouldn’t be that hard.

  13. Hi, great tutorial. I think there’s a small bug for the infinite scroll demo though.

    When you’re on the “Show All” tab and click “Load More”, the article “Add Wistia videos …” shows up.

    Then if you click on another tag and then click back again on “Show All” and then “Load More” again, another article populates, “Pre-populate Woocommerce…”. So the “Show All” functionality is not resetting itself. Once you have loaded new posts once, they are gone forever (unless you refresh the page).

    I am trying to fix that but haven’t been able to yet. Any ideas?

  14. Hi Vlado,
    Excellent tutorial, great work!

    However there is an issue with the infinite scroll (I tested on your demo too, just to be sure it was not coming from me).
    Basically, it doesn’t reset to the “Load more” link to “#page-2” when you change of tag. For example on your demo, stay on “show all” and then navigate to #page-5; then pick another tags (less than 5) and try to click on “Load More”, it will be set with “#page-6” and will show “You reached the end”.

  15. Hi Again,

    I fixed the issue I just reported, I placed the ( condition after the else {}.

    * Append content for infinite scroll
    else {
    if ( !== 0) {
    $pager.attr(‘href’, ‘#page-‘ +;

    1. Thanks Nico,
      I will review the code as soon as I get some free time.
      I’m sure it worked when I was testing it :)

      Glad you found your own solution, it fulfills the purpose of this tutorial.

    2. Mhm. I have just checked this and it’s working for me.
      Would you mind sharing what browser and version are you using?

  16. Hi,

    Using Chrome Version 56.0.2924.87. + FF 52.0.2
    Do this little experiment:
    1. Go to
    2. “Show All” is selected, click on “load more” twice (mouse hover the “load more” button and you will see next link is “#page-4”)
    3. Select 2 another tags, you will see the first one, then click “Load more” to see the second one and you will get “You reaced the end” message because it’s trying to load “#page-4” instead of “#page-2”
    4. Also, if you go back to “Show All”, you get to see the first post again, but when you click on “Load more” it will bring the fourth one (“#page-4”), skipping post 2 and 3.

    Hope it make sense :)
    Thanks again for your tutorial!

Leave a Reply

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