Filter WordPress posts by custom taxonomy term with AJAX and pagination

This is a repost of original post AJAX Filter Posts By Tag.
There was a lot of interest about that post so I decided to further improve code example and make it more simple.

In this example I will list all posts by taxonomy post_tag using a shortcode, example also includes pagination.
This example will filter all posts by a single tag only, meaning you are not able to filter posts by multiple tags at a same time, I will cover that case in the next tutorial to keep things simpler to understand.
Here you can check working example of Filter WordPress posts by custom taxonomy term with AJAX.

1. Shortcode

Shortcode is used to display list of available tags and container where posts will be inserted after we get them with AJAX.
Shortcode accepts 4 parameters:

tax
taxonomy we want to get terms from
terms
list of comma separated term_id’s, in case you would like to enable filtering by only certain terms
active
term_id you would like to be initially selected when page loads
per_page
how many posts will be displayed per page

Usage in WordPress page: [ajax_filter_posts per_page="1"]

Or if you would like to use it in your template file: <?php echo do_shortcode('[ajax_filter_posts per_page="1"]'); ?>

Basically this is just a get_terms function from WordPress Codex and some additional markup that is needed to make this work properly.
Each anchor tag contains taxonomy and term slug in data attribute, this is required so we can get these data with jQuery and get results for clicked tag.

If you look at the source you will notice that tag looks for example like:
<a href="?path-to-tag" data-tax="post_tag" data-term="term-slug">

2. Javascript

Javascript file consists of 2 parts.

  1. get_posts($params) which makes AJAX request and returns results
  2. Binding get_posts function to tag list and pagination

On click…

On click we need to get taxonomy, slug and page number. We need these so we can construct WP_Query.
These parameters are then passed to our custom javascript get_posts($params) function.

get_posts – make a call and display results

I wanted to keep function very basic, just to get and display data, you can add the fancy stuff yourself.
Firstly we define selectors, then pass the params, do ajax and display result if any.
If there will be error, it will be shown in status div.

3. WP_Query and pagination

First of all, if we want to make pagination work we need to slightly modify WordPress function.
The idea is that when someone clicks on pagination link, from that link we need to extract only page number we want to retrieve.
To make this happen we use WordPress function paginate_links to generate exact markup we need.

Custom pagination markup

The following function will generate page links with following markup:
<a href="?paged=1">

And it’s very easy to get page number from this link with jQuery (we do that in step 2 – On click…).
We get ‘href’ attribute from clicked link and strip all except numbers to get our page number.

WP_Query

This is the function we call with javascript which creates query and returns post in JSON format.
I will not explain to much, you should be able to see from the source what’s going on there.

  1. Verify nonce
  2. Sanitize inputs
  3. Construct WP_Query and do the standard WP loop
  4. Return result

And you are done

Here you can check working example of Filter WordPress posts by custom taxonomy term with AJAX

Downloads

Full source code in single file can be found on github:

  1. PHP file gist
  2. JS file gist

Just keep in mind that you need to enqueue and localize your javascript file.

55 Comments

  1. Hi, thank you for tutorial, in the previous tutorial about ajax , without pagination,
    i’m getting only one post displayed, after clicking in tag, even if i have 10 posts in tag.
    How to get this filtering working without using shortcode?
    I’ve read that using shortcodes are bad for performance, beacase wp will look in all shortcodes,
    and this may slow down page, if you using them often
    If i change adding class active into toggleClass , then can i have filtering by many tags at once working?

    1. Hi Gabrielle,

      1. If you don’t want to use shortcode just call the function vb_filter_posts_sc() and pass params as an array.
      2. changing ‘toggleClass’ will not enable filtering of multiple tags, it picks up value only of clicked tag, but instead you would need to pick up all tags with class ‘active’

    1. Hi Gabrielle,

      After you get all :checked elements with jQuery you can simply save them to local storage with javascript:

      // Put the object into storage
      localStorage.setItem('myTerms', JSON.stringify(myTermsObj));

      // Retrieve the object from storage
      var retrievedObject = localStorage.getItem('myTerms');

      // View terms in console
      console.log('retrievedObject: ', JSON.parse(retrievedObject));

    1. It would be possible but you need to update html/js code slightly:
      1. in HTML id of containers should be unique
      2. js function on click get_posts you should pass the container then as well so when returning posts with AJAX they sit in correct place, eg: get_posts($params, $(‘.closest-container’));

  2. Thanks for sharing all of this. I used your original post on a project about a year ago, and I was excited to find the updated version for my latest endeavor.

    How hard would it be to add a “View All” link that would list all posts with the given taxonomy and not filter anything out?

  3. When i use your example, the user has to push the ‘all-terms’ buttons in order to see all posts right?

    is it possible to set data-term=”all-terms” onLoad?

    1. As said above, it should work if you for example do it with jQuery like: $('a[data-term="all-terms"]').trigger('click');
      Do it inside document.ready .. basically you trigger a click on button instead of user on page load.

    1. Hello,

      Use the shortcode:[ajax_filter_posts per_page=”1″]per_page stands for how many items per page.

  4. Hello Vlado, thank you for this enlighting example.
    However, there’s something I don’t understand in my imlementation: I’m always getting 0 (plain number) as AJAX response (data.msg), and I’m really trying to figure out why.
    The response status code is 200 and the ststausMessage is “success”, so I suppose that the request is correctly processed.
    I’ve tried the same query statically and it returns the correct and expected result, so I suppose that everything in the process is ok, maybe somewhere with the AJAX is wrong? I’ve really got no idea.
    Think you can give me a hint?

    Thanks

    1. I discovered the AJAX request wasn’t correctly processed because I put “`vb_filter_posts()“` inside the template and not into functions.php

      To debug the AJAX request it came to help using the error log: “`error_log( ‘AJAX request check’ );“`

      by defining in “`wp-config.php“`:

      “`
      define( ‘WP_DEBUG’, true );
      define( ‘WP_DEBUG_DISPLAY’, true );
      define( ‘WP_DEBUG_LOG’, true );
      “`

      and then checking the file “`/wp-content/debug.log“`

    2. Hi Andrea,
      Glad you found your solution. And yes, all of the code should be included in functions.php.
      If you get 0 as AJAX response it always means that function you are calling doesn’t exist.

  5. I have two levels of the taxonomy
    Level 1 (parent)
    sublevel 2 (child)
    I want to have a list of only children taxonomy
    Understand?
    Or drop-down list
    – Level 1
       – sublevel
    How to display only a sub?

  6. Hello

    i have left comment before about infinity scroll :)

    i received email that response from you has been sent but couldn’t find it. :)

    My question was can we implement infinity scroll + can we do some filtering by category + by time in hours :)

    Pozdrav iz Crne Gore :)

  7. Hi

    thanks for your replay!
    I have sent you email if you could please check :)

    I pasted php code from your PHP file gist into functions.php
    copied JS file gist into child theme folder and added following lines at the end
    of functions.php file but on frontend i get only shortcode displayed :(

    regards!

    1. Hi Jeris,

      It means you didn’t include files well because shortcode doesn’t exist, therefor it cannot be parsed.

    2. Hi! I had this same problem and I spent hours trying to find a solution. At the end it was a simple one. I removed the empty spaces around [] -characters, that can be seen also here: (‘[ajax_filter_posts per_page=”1″]’).

  8. Hi Bobz,

    The code is working perfectly. How do i customize the pagination to use just Previous and Next? without the page numbers?

    Thank you!

  9. Hi Vlado,

    I’m experiencing the same issue as @Nikola and @Jeris where shortcode is only echo and not executed…
    here is my function.php file http://termbin.com/v6v5
    here is how i’m calling the shortcode in my personal category-template.php http://termbin.com/v6v5
    From i clearly understand i’m doing something wrong in the enqueue and or localization but i don’t understand where is my mistake…
    I’ve forked 2016 wordpress built-in theme and hack from here, am i having conflict somewhere ?

    Sorry for the newbies questions, but i would really appreciate some guidance here :D

    Thanks

    Matth

    1. Hi Matth,

      Sorry for late response. If you just copied it from the above, then maybe the problem is that there is extra space on both sides, it should be
      echo do_shortcode('[ajax_filter_posts per_page="1"]');

  10. First of all, nice job, I’m loving this feature. It made my life easier.

    I’m using it to load a series of posts for a portfolio of one company.
    However, I was wondering whether it’s possible for it to have an extended feature. Is there a possibility of adding a search input field to filter the posts by their title.
    Say we have a load of posts to filter with tags but beside that the client can search their posts via post title.

    1. Hi Arbias,
      Sure it is possible, but it goes beyond this tutorial.
      You need extend js and wp_query part by one field, it’s an extra param.

  11. My friend Thank you for this wonderful explanation. But I would like to ask you how to do it to custom post type because I tried but shortcode doesn’t work>
    Again thank you

    1. This example doesn’t show how to use it with custom post type or taxonomy, but from the source code you should be able to alter the code for your needs.

    1. Hi Francesco,
      Something not included maybe, please turn debug on you should be able to see and fix your problem.

    1. Hi Razi,
      Something not included maybe, please turn debug on you should be able to see and fix your problem.
      Check php error log too

  12. Hi Vlado, your code is awesome! Really thanks!
    I need to trigger a matchHeight function after ajax was loaded… i’ve found this workaround but sometimes fails… any suggestions?

    jQuery(document).ready(function($){

    $(function() {
    $.fn.matchHeight._maintainScroll = true;
    $(‘.display-tour-bottom’).matchHeight();
    });

    $(document).ajaxComplete(function() {

    $(‘.display-tour-bottom’).matchHeight(‘remove’).matchHeight({ property: ‘min-height’ });

    });

    });

    1. Hi Jack,
      Maybe call matchHeight in ‘complete‘ part of ‘$.ajax‘ call in my script, should be same or similar like yours.
      Other thing I can think of is maybe trigger window.resize() after eg. 500ms in complete.
      As far I can remember matchHeight triggers after resize too so it might be helpful.

Leave a Reply

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