Category Navigation Links
In a previous post I added navigation links. I liked the solution because it was simple, but since it was working only chronologically sometimes it didn’t make sense where the links would take you. The next post sometimes wouldn’t relate to the current one, and if the current one is a post that belongs to a series of posts that are closely related, the link needs to take you to the exact correct place.
Solution Concepts
I decided the previous/next post should be the previous/next post in the same category. I wanted the URL and link text accessible through the front matter, to keep the liquid code simple. I used a hook to make that happen (plugins/assign_navigation.rb
).
For the edges of the category posts (first and last posts of a category) I decided to add a link to the category page. This link, differently from the previous/next links, is not specific to a post but specific to a category (common for all posts in a category). I added that information in a data file (_data/categories-metadata.json
).
In the end not only the new hook was needed but I also had to do two updates to the old hook (_plugins/assign_categories.rb
) that assigned categories: first, to make it run earlier (as :site, :post_read
) for the categories to be used later by the new hook, and second to also bring the edge cases data from the data file to the post front matter (this is used by the liquid script when previous/next is nil).
Solution Code
The liquid code now looks like this (the next/previous data is composed of front matter variables that come from the new hook, and edge cases are front matter variables that come from the old hook, now updated, using the data file):
The old hook for assigning categories, now updated, looks like this:
# _plugins/assign_categories.rb
Jekyll::Hooks.register :site, :post_read do |site|
puts "Assigning categories based on folder structure after posts are read"
categories_metadata = site.data['categories-metadata']
site.posts.docs.each do |post|
# Extract the category from the post's directory - that will be the category name
dirname = File.basename(File.dirname(post.path))
category_name = dirname
# Assign the category to the post's front matter
if category_name != "_posts" && (post.data['categories'].nil? || post.data['categories'].empty?)
post.data['categories'] = [category_name]
puts "\tCategories assigned to post #{post.data['title']}: #{category_name}"
# Add category related data from _data dir
if categories_metadata.key?(category_name)
metadata = categories_metadata[category_name]
post.data['category_page_title'] = metadata['page_title']
post.data['category_page_url'] = metadata['page_url']
puts "\tAdded category metadata :\n\t\tCategory page title `#{post.data['category_page_title']}`, \n\t\tCategory page url `#{post.data['category_page_url']}`"
else
puts "\tNo metadata found for category #{category_name} in categories-metadata.json"
end
end
end
end
And this is the category navigation links hook:
# _plugins/assign_navigation.rb
Jekyll::Hooks.register :site, :pre_render do |site, payload|
puts "Starting Category Navigation Generator"
site.posts.docs.each do |post|
puts "\tProcessing post: #{post.data['title']} in categories: #{post.data['categories']}"
post_categories = post.data['categories'] || []
category_posts = site.posts.docs.select do |p|
p_categories = p.data['categories'] || []
p_categories.sort == post_categories.sort
end
category_posts.sort_by! { |p| p.date }
current_index = category_posts.index(post)
if current_index
if current_index > 0
post.data['previous_post'] = category_posts[current_index - 1]
puts "\t\tPrevious post: #{category_posts[current_index - 1].data['title']}"
else
post.data['previous_post'] = nil
puts "\t\tPrevious post: nil"
end
if current_index < category_posts.length - 1
post.data['next_post'] = category_posts[current_index + 1]
puts "\t\tNext post: #{category_posts[current_index + 1].data['title']}"
else
post.data['next_post'] = nil
puts "\t\tNext post: nil"
end
end
end
puts "Category Navigation Generator finished."
end