I’ve just started to learn Jekyll as I rework my site. With over a couple hundred blog posts that cover a few dozen categories, it was important to present the ability for a user to see lists of blog entries by category.
The general idea:
-
Render a series of buttons (which could also be text links), one for each category.
-
On clicking a button, call a function that iterates through all site posts. If the clicked category appears in a given post’s list of categories, display the corresponding blog entry, otherwise remove it.
Sounds easy, right? The resulting code is fairly simple; getting there was just a little trickier. I’ll lay it out piece-by-piece, then provide the entire page source.
Displaying the series of category buttons is mostly Liquid + HTML:
<button id="All" onclick="filterUsingCategory('All')">
*Show All Posts*
</button>
{% assign categories = site.categories | sort %}
{% for category in categories %}
{% assign cat = category | first %}
<button id="{{ cat }}" onclick="filterUsingCategory(this.id)">
{{ cat }}
</button>
{% endfor %}
The Jekyll docs aren’t terribly clear, but looking at the source reveals that site.categories
is a key-value-pair data structure (a Ruby Hash), where the key is the category name and the value is the list of posts. Sorting site.categories
thus sorts by the key, which is the category name. Applying the first
filter to category
returns that category name.
Each button gets an id which is the unique category name itself. When clicked, the button’s id gets passed to the filterUsingCategory
JavaScript function.
Displaying the list of all posts requires only a small change:
{% assign id = 0 %}
{% for post in site.posts %}
{% assign id = id | plus:1 %}
<div class="post" id="{{id}}">
<p class="itemInteriorSection">
<a href="{{post.url}}">{{ post.articletitle }}</a><br />
<!-- more post details here -->
</p>
</div>
{% endfor %}
Each post needs a unique ID so that its div
(containing the blog summary entry) can be found and either shown or not. The easiest technique was to track an id
that increments each iteration through the Liquid loop {% for post in site.posts %}
.
The JavaScript filter function mixes & matches Liquid and JavaScript:
<script type="text/javascript">
function filterUsingCategory(selectedCategory) {
var id = 0;
{% for post in site.posts %}
var cats = {{ post.categories | jsonify }}
var postDiv = document.getElementById(++id);
postDiv.style.display =
(selectedCategory == 'All' || cats.includes(selectedCategory))
? 'unset'
: 'none';
{% endfor %}
}
</script>
The category selected by the user (by clicking on one of the buttons) is passed as an argument to filterUsingCategory
.
Code in the function iterates through site.posts
and once again tracks an ID that increments each time through the loop, so that we can retrieve each post’s corresponding div
. For each post, the first step is to convert the Liquid array of posts to a JavaScript array by using the jsonify
filter. Next, the post’s div
element is retrieved by using the (pre-incremented) id. Finally, the div
is either included if the category is 'All'
or if the post’s categories contains the selected category.
You’ll want to add appropriate styling of course.
I’ve posted a gist of my blog page’s index.md. It contains all of the above code in context and in an appropriate order.