WordPress Filter Posts with Ajax without page reload or plugin | Weichie
Menu

Updated on September 16, 2020

·

WordPress Filter Posts with Ajax, without page-reload or plugins

WordPress Filter without page reload using ajax is not a hard thing to do. Many developers will quickly install another plugin like facetwp or yith to do things like this. But it’s actually pretty simple, as WordPress has default ajax functionalities. For me, as a front-end developer, the hardest part is making the actual WP_Query if we’re extending this method for combined filters WooCommerce.

A more advanced ajax filter for Woocommerce will follow soon! Let’s first start with the basics. A live example of ajax filters in WordPress can be found on one of my live projects: https://welmanprojects.be/projects/ (currently still in development)

WordPress post filters with ajax

WordPress setup for posts and custom post types

Filtering posts, pages, or custom post types with ajax all works in the same way. I assume you have a basic knowledge of javascript and PHP for the syntax and basic WordPress to know how to make a query. You can filter posts and post types by category, tags, and custom taxonomies.

In this example, I will show you how we can filter our custom post type ‘projects’ by category, based on the category slug. There are many ways to query the posts. I’ll give a quick overview of all possible arguments so you can see which ones work best in your project.

Available WP_Query Arguments

Category Parameters

  • cat: use category ID – int
  • category_name: use category slug – string
  • category__and: use category IDs – array
  • category__in: use category IDs – array
  • category__not_in: use category IDs – array

Tag Parameters

  • tag: use tag slug – string
  • tag_id: use tag ID – int
  • tag__and: use tag IDs – array
  • tag__in: use tag IDs – array
  • tag__not_in: use tag IDs – array
  • tag_slug__and: use tag slugs – array
  • tag_slug__in: use tag slugs – array

WordPress Page Setup

To get started, we first need a page that queries all our projects and displays them on that page. We also need to query over our categories, to be able to display all available categories. A live example of the filters can be found on the website of a client of mine: https://welmanprojects.be/projects/ (still in development)

Let’s display our available categories first:

<?php $categories = get_categories(); ?>
<ul class="cat-list">
  <li><a class="cat-list_item active" href="#!" data-slug="">All projects</a></li>

  <?php foreach($categories as $category) : ?>
    <li>
      <a class="cat-list_item" href="#!" data-slug="<?= $category->slug; ?>">
        <?= $category->name; ?>
      </a>
    </li>
  <?php endforeach; ?>
</ul>

Notice that our first list-item is not within the loop. We’ll use this to remove the filters and display all the posts again. In our anchor-tags you see href=”#!”. We need this because I want to use a-tags for the filters, but anchor tags require a href-tag to be a valid HTML element.

Also note that I am using data-slug in my list-item links. We’ll need this later in our functions to filter on the category slug.

Querying over WordPress Posts and Custom Post Types

<?php 
  $projects = new WP_Query([
    'post_type' => 'projecten',
    'posts_per_page' => -1,
    'order_by' => 'date',
    'order' => 'desc',
  ]);
?>

<?php if($projects->have_posts()): ?>
  <ul class="project-tiles">
    <?php
      while($projects->have_posts()) : $projects->the_post();
        include('_components/project-list-item.php');
      endwhile;
    ?>
  </ul>
  <?php wp_reset_postdata(); ?>
<?php endif; ?>

This is a basic WP_Query to pull in all our posts from post-type ‘projecten’. Make sure our single list items are coming from a separate PHP file. You can use include like I did, or use get_template_part(). Whatever you prefer.

The include file is just a single <li> item, so you can give it whatever markup you want. I won’t include it in this tutorial, because we’re going straight to the point: filtering

Feel free to reach out to me or leave a comment in case you want to see a full setup, that also explains the project-list-item.php file. For testing your code, you can add the_title(); in this file for now.

The WordPress filter with Javascript!

For me as a front-ender, obviously the most fun part! We’ll be running a filter function when we click on a category link we’ve made before. We’ll be doing 2 things:

  1. Make the clicked category the new active filter
  2. Display only the projects that belong to that category

EASY. But let’s not celebrate too early. This is only the JS part, we do need some custom PHP code later on in order to make this stuff work.

$('.cat-list_item').on('click', function() {
  $('.cat-list_item').removeClass('active');
  $(this).addClass('active');

  $.ajax({
    type: 'POST',
    url: '/wp-admin/admin-ajax.php',
    dataType: 'html',
    data: {
      action: 'filter_projects',
      category: $(this).data('slug'),
    },
    success: function(res) {
      $('.project-tiles').html(res);
    }
  })
});

This doesn’t look too complicated, does it? We click our category links and create a new $.ajax call. We will be posting our data to a custom function called ‘filter_projects’. We also need to post the slug we want to filter and we do that by passing our filter to our data ‘category’.

The AJAX URL is default in wordpress: /wp-admin/admin-ajax.php

The PHP WordPress Filter, the horror!

Just kidding. Ajax queries in WordPress with PHP all sounds like a nightmare, but it’s all fun and games! Because we are using the default /wp-admin/admin-ajax.php URL, we can write this function somewhere in the functions.php file:

function filter_projects() {
  $catSlug = $_POST['category'];

  $ajaxposts = new WP_Query([
    'post_type' => 'projecten',
    'posts_per_page' => -1,
    'category_name' => $catSlug,
    'orderby' => 'menu_order', 
    'order' => 'desc',
  ]);
  $response = '';

  if($ajaxposts->have_posts()) {
    while($ajaxposts->have_posts()) : $ajaxposts->the_post();
      $response .= get_template_part('templates/_components/project-list-item');
    endwhile;
  } else {
    $response = 'empty';
  }

  echo $response;
  exit;
}
add_action('wp_ajax_filter_projects', 'filter_projects');
add_action('wp_ajax_nopriv_filter_projects', 'filter_projects');

When hitting the ‘filter_projects’ function, we’ll be checking if we did POST our category-slug. If it’s an empty string, the query won’t filter and it will return all our posts. (we use it when we click the filter “show all projects”)

Next, we run our WP_Query again, but this time with an extra parameter for the category name. Ironically enough, it’s using the SLUG and not the name of the category. Afterward, we just loop over query results and add the template part as a string to our $results.

Don’t forget to register our function, so WordPress knows our ‘filter_projects’ exists. Otherwise, we’ll receive an error when we try to make our ajax call.

Filter Dynamic custom post types with ajax

If you are using multiple post-types that are using the same taxonomies, we can dynamically pass our post_type to our ajax filter function as well. This way we can reuse our previously setup for all posts types on our page.

To be able to do this, we take our PHP list setup from the example above and add a new data attribute ‘data-type’ to also add the post type:

The following code snippets display only the parts that changed from the code examples from before. If something is not working correctly, make sure to use the code from above to setup all the triggers and functions.

<li>
  <a class="cat-list_item" href="#!" data-slug="<?= $category->slug; ?>" data-type="projecten">
    <?= $category->name; ?>
  </a>
</li>

Our data-type can be dynamic as well of course if you’re using the same filter component on all pages. I’ve kept it static in my example for simplicity. Now you can create multiple lists for each post-type and change the data-type attribute to the post type you want to filter.

In our javascript function, we also add an additional parameter in our Ajax filter function:

$.ajax({
  type: 'POST',
  url: '/wp-admin/admin-ajax.php',
  dataType: 'html',
  data: {
    action: 'filter_projects',
    category: $(this).data('slug'),
    type: $(this).data('type'),
  },
  success: function(res) {
    $('.project-tiles').html(res);
  }
});

And at last, we need to make sure our PHP code uses our dynamic post type argument to filter our post types. Update the PHP function code in functions.php to look like this:

function filter_projects() {
  $postType = $_POST['type'];
  $catSlug = $_POST['category'];

  $ajaxposts = new WP_Query([
    'post_type' => $postType,
    'posts_per_page' => -1,
    'category_name' => $catSlug,
    'orderby' => 'menu_order', 
    'order' => 'desc',
  ]);
  $response = '';

  if($ajaxposts->have_posts()) {
    while($ajaxposts->have_posts()) : $ajaxposts->the_post();
      $response .= get_template_part('templates/_components/project-list-item');
    endwhile;
  } else {
    $response = 'empty';
  }

  echo $response;
  exit;
}
add_action('wp_ajax_filter_projects', 'filter_projects');
add_action('wp_ajax_nopriv_filter_projects', 'filter_projects');

WordPress Ajax Filter Summary

This is all we need to make ajax filters work in WordPress. Let me know if the ajax filters were easier than you thought or if you’re still having difficulties.

A more advanced, dynamic ajax filter for Woocommerce will follow somewhere next month. I still have some difficulties with filtering e-commerce products with ajax to make it super fast. The post will be ready when I’m done struggling. 😉

Leave a Reply

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

6 Comments

  • Marty says:

    I want to see full code. Contact me please on my email. Where did you put the files you mentioned? I also plan to put snippets instead of intrusive code injection inside functions.php. I would try to avoid putting code inside admin-ajax.php also, just beacuse of possible updates and exploits. And one comment also, I really like php more, so when possible, I would bypass the ajax. It runs faster, eats less memory. If you do a lot of JavaScript and jQueries, it’s gonna eat your resources.

    • Bob Weichler says:

      Hi Marty, I will try adding some files to a git repo to offer a full code example. Also, I am a front-end developer so I will always choose for a JS solution when possible 🙂 While with javascript I can rerun solely the query I need, running it in plain PHP will require a full page reload – reloading headers and footers ass well. I did not add any code to admin-ajax.php, but it’s the WordPress default route for ajax calls.

  • Dee says:

    Hi.
    Thank you for your work on this.
    Could you explain how this could work for post_tags?
    in the js, changed the data: category to data : taxonomy
    in functions filter_projects, changed the slug variable to –
    $catSlug = $_POST[‘taxonomy’];
    But it shows ALL the posts on the site

    • Bob Weichler says:

      Hi Dee,
      Did you also change the WP_Query from ‘category’ to ‘tag’ => $catSlug? It should work just fine for post_tags as well. Just make sure you’re using the correct WP_Query arguments. They are slightly different from categories to tags. You can find all available query arguments for tags in the beginning of my post.
      Hope it helps!

  • Bart says:

    Hello, does this also work for multiple filters? So can I copy the code that multiple filters work on the same custom post?

    Thanks in advance,

    Bart

    • Bob Weichler says:

      Hey Bart,
      Thank you for your message. Do you mean combining multiple filters or using this filter for different post-types? You can pass the parameter of ‘post_type’ to your PHP function the same way as we pass our ‘category_name’ in our example. I will edit this post with add an additional example, but hopefully, this makes sense?
      Best,
      Bob