Implementing an Animated Filtering of Posts by Category

Ever wanted to make a nice column-presentation of posts with category filters above that shuffles and filters the posts with an nice animation? Then this is the tutorial for you.

What we will make in this tutorial is a list of all posts (preferably within a custom post type such as employees) nicely in columns and with their featured images. Above the posts we generate filters for the connected taxonomy. Upon clicking a filter, the posts will with a nice animation shuffle and hide posts that are not present in the chosen category.

To do this we make use of a Javascript library for the filtering. There are many available – some require jQuery and some don’t – some are open source and some require commercial license. The most popular ones are MixItUp and Isotope. However both of these require a commercial license. For my project I needed a library that does the trick while being completely free to use in commercial projects. I’ve tried several and the best one I’ve found so far is FilterizR. Let’s start this tutorial by getting ahold of this library!

The FilterizR library

Check out the demo at FilterizR homepage to see if this library is for you. You can even play with the options real-time. This library supports filtering (which is what we’ll do in this tutorial), shuffling (randomizing order), searching, and sorting. It’s responsive and optimized for mobile devices. So far from what I’ve experienced it also manages setting the auto-height on the container on each item very well.

FilterizR offers three different types of usage:

  • Vanilla Javascript: Exposes the FilterizR as a global function to call in standard Javascript.
  • jQuery: Allows you to use jQuery to initialize and set it up.
  • Pure Javascript library/npm: For use in ES6 imports (e.g. if you write custom blocks for Gutenberg).

The second option is the easiest for people unfamiliar with Javascript. Most people start with jQuery. But keep in mind that jQuery is a large and heavy library to load in your project, and if it can be avoided, you should. In this tutorial we’ll use the vanilla Javascript method. If you are implementing this in a custom Gutenberg block, you should consider using the third option. Then you can simply install it via npm and import the library in your block code.

Downloading and setting up the files

Let’s get ahold of the appropiate file! Go to FilterizR’s Github to download. Unless you are installing via npm (third option), you can go into the folder ‘dist‘ and download the minified library you need. As I will use it with vanilla Javascript in this tutorial I’ll download the vanilla.filterizr.min.js file and place it into my theme-folder/assets/js/ folder. Where you place the library is of course completely up to you, just adjust the path further down.

We will also need another Javascript file to initialize and decide the options. I’ll create a file theme-folder/assets/js/filtering-main.js. We’ll return to this file later on. This is of course not necessary if you are implementing this in a custom Gutenberg block.

In order to make WordPress include the scripts we need to enqueue both scripts in the wp_enqueue_scripts hook. This can be added somewhere in your theme’s functions.php. Like so:

add_action('wp_enqueue_scripts', function() {
	wp_enqueue_script('filterizr', get_template_directory_uri() . '/assets/js/vanilla.filterizr.min.js', [], false, true);
	wp_enqueue_script('filtering-script', get_template_directory_uri() . '/assets/js/filtering-main.js', ['filterizr'], false, true);
});

Adjust the filenames and/or paths to fit your project/theme. The above code enqueues the vanilla filterizR script, and secondly the filtering-main.js script which has a dependency to the filterizR script. This makes sure the initializing script is loaded after the necessary library.

Rendering the template

The next step is having a place where we want to render this list of posts. This is entirely up to you. To keep it simple in this tutorial I’ll use a page template in my theme. If you are implementing this in a Gutenberg block, you can render it either with PHP (for dynamic blocks) or with Javascript in the block’s save method. The important part is to render the HTML surrounding the posts and filters correctly.

Rendering the filters

I’ll create a page template template-filters.php in my theme folder and add my PHP code to render the output there.

In this tutorial we assume we want to render posts within a custom post type. You can do this with standard posts and categories, but posts will usually become quite many – which is not feasible to use for this kind of display. Assume for example a custom post type for employees with a connected custom taxonomy for department. I won’t show how to add the custom post type in this tutorial, just keep in mind that the post type name is employees and the custom taxonomy name is department. Switch out the names for your post type and taxonomy. If you are unsure how to add a custom post type I have a tutorial post on how to create custom post types and taxonomies.

In my template I start by generating the filters. I’ll fetch all non-empty terms in my custom taxonomy using get_terms() and add them in an unordered list.

<?php
$departments = get_terms([
	'taxonomy' => 'department'
]);
?><ul class="filter-controls">
	<li data-filter="all"><?php _e('All', 'txtdomain'); ?></li><?php
	foreach ($departments as $department) {
		?><li data-filter="<?php echo $department->slug; ?>"><?php echo $department->name; ?></li><?php
	}
?></ul>

Before generating the elements for each term in the taxonomy I’ll make sure to make one element for “All” (line #6). To make FilterizR filtering work we need to provide the data-attribute “data-filter” to each filter element. The content should uniquely signify an unique filtering value, in our case the term’s slug (you can use term ID or something else if you want). This means I need to add the term’s slugs as data-attributes to the posts as well (see later) so FilterizR can know which posts belong to which filter.

With the above code you should get a list with “All” followed by all non-empty terms. Great!

Now, just below this I’ll start to render the posts.

Rendering the posts

When rendering the posts you need to remember two things. One is to wrap all posts inside a container, which I’ll give the class name filter-container. This class is what we need to target with Javascript to make FilterizR work. And second each item needs to have a data attribute “data-category” that list out all terms separated by comma. The values here must correspond to the values you generated in the filters’ “data-filter” attribute.

I query all posts from the custom post type with WP_Query and loop through the posts. What you display per post is completely up to you, but the example below renders the featured image, the post title and a single custom post meta for job title. All wrapped inside links that goes to the single employee.

<?php
$employees = new WP_Query([
	'post_type' => 'employees',
	'posts_per_page' => -1,
	'orderby' => 'name',
	'order' => 'ASC'
]);
if ($employees->have_posts()) {
	?><div class="filter-container"><?php
	while ($employees->have_posts()) {
		$employees->the_post();

		$dep = wp_get_object_terms(get_the_ID(), 'department', ['fields' => 'slugs']);
		?><div class="filtr-item" data-category="<?php echo implode(',', $dep); ?>">
			<a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php
		
		if (has_post_thumbnail()) {
			the_post_thumbnail('employees-thumb');
		}

		$job_title = get_post_meta(get_the_ID(), 'job_title', true);
		?><div class="employee-info">
			<h2><?php the_title(); ?></h2>
			<span class="job-title"><?php echo $job_title; ?></span>
		</div><?php


		?></a></div><?php
	}
	wp_reset_postdata();
	?></div><?php
}
?>

The query at line #2-7 makes sure I fetch all published posts of the post type employees, ordered alphabetically. Before rendering the wrapping div for each post, at line #13, I use wp_get_object_terms() to fetch all terms assigned to this post. As parameter I ask for only the terms’s slugs. The return is an array of any assigned term slugs. I then use the PHP function implode() to create a string from all elements in the array separated by comma. This is echoed as the required data-category attribute to the root div – which makes the filters work.

As for the content for each post the output is pretty standard. We generate the post’s featured image using a custom image size (‘employees-thumb‘), the post title and the value of a single custom post meta (for job title). Everything is wrapped inside a link that goes to the single view for that employee. I also added some classes and wrappers to make it easier to target with CSS. The output for each element is of course completely up to you.

At this point the PHP render should be complete. You are welcome to style and make it look nice, but don’t bother styling columns. FilterizR will generate columns (flexbox) for you. The next step is actually initializing the filtering script!

Initializing the filter script

The last part is editing the filtering-main.js script. We simply need to tell FilterizR to initialize filtering on our rendered content. There’s a few things to keep in mind here though:

FilterizR will make sure the wrapping container of all posts is auto-sized to fit the view. For example if the active filter displays posts in one row and we click another filter that has four rows, the container will autoscale to the correct height. Thus making sure that any content that comes after doesn’t get hidden behind. But in order for this auto-resizing to work, all images must have been loaded first. In some cases the script may be loaded and run before each image is finished loading, and this causes the container to auto-collapse to a height of 0. This is no good. So we need to place the initalizing code inside a function where we know images are done loading. For vanilla Javascript this is window.onload. For jQuery you would use $(window).load().

Secondly we want to make sure our script only initializes FilterizR if there is indeed a filterable container present. If we initialize FilterizR and the current page doesn’t have the necessary wrapper class it will result in a Javascript error. We can solve this by checking if the container class exists first. In vanilla Javascript you can use document.getElementById() or document.getElementsByClassName(). See example below. For jQuery you would use jQuery('<selector>').length to check this.

The way to initialize FilterizR by vanilla Javascript is creating a new instance of Filterizr, providing the container selector as first parameter and optionally an object of settings as second parameter. This is an example of the most basic:

filtering-main.js
window.onload = function() {
	var filterContainer = document.getElementsByClassName('filter-container');
	if (filterContainer.length > 0) {
		filterizr = new Filterizr('.filter-container');
	}

}

The initializing happens at line #4. We provide the class name to the container that wraps around our posts with a dot in front, signifying a class selector. With the above code FilterizR should now take over your posts and place them in columns. Because we used the appropriate data-attributes the filters should automatically work as well!

Customizing FilterizR’s options

I have just a few final tips to optimize FilterizR, which we can do by providing a settings object. I recommend taking a look at the possible options for FilterizR to see what you can do. You can control the animation speed, animation type, and more!

In order to make the columns work optimally in responsive sizes, I’ve found that setting layout to ‘sameWidth‘ makes FilterizR work better with posts that have different heights. This results in a “Masonry” style. In our example we echo out additional content after the image, and this can result in varying heights (names can be longer forcing it into multiple rows). This can result in some strange behavior.

As default each item gets no space between each other. If you wanted some spacing between each item, you can set the gap size in px for gutterPixels. In the example above I’ve used 10px as gap size. PS: The gap size is also added to the wrapping container. This might not always be what you want. To override this I’ve simply added styling padding: 0!important onto .filter-container.

Here’s the adjusted code for initializing FilterizR with my options:

filtering-main.js
window.onload = function() {
	var filterContainer = document.getElementsByClassName('filter-container');
	if (filterContainer.length > 0) {
		var options = {
			layout: 'sameWidth',
			gutterPixels: 10,
		}
		filterizr = new Filterizr('.filter-container', options);
	}

}

With some minor styling the result is something like this:

Keep in mind that filtering is just one thing FilterizR can do! Check out the examples under ‘Tutorials’ on their home page. You can add controls to sort, shuffle, and/or search. And you have more options to control the layout as well.

Leave a comment