How to link post categories in Jekyll - jekyll

I've got the following code in my index.html for Jekyll. I'm trying to find a way to link the categories associated with each post to the actual post themselves. So, if a post contains the category "travel" I want to click on a link that says "travel" which will bring me to all posts categorized as such.
<ul class="post-list" style="list-style-type: none;">
{% for post in paginator.posts %}
{% unless post.categories contains 'portfolio' %}
<li>
<h3>{{ post.title }}</h3>
<span class="post-meta">{{ post.date | date: "%c" }}</span>
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }} //problem area
{% endfor %}
{% endunless %}
{{ post.excerpt }} Find out more...<br><br>
</li>
{% endunless %}
{% endfor %}
</ul>

Figured it out. For anyone else wondering how to do the same, first setup a categories.html page in your root directory. This page will list all posts that meet a specific category. It does by turning the category names into named anchor slugs as such <a href="#{{ category | first | remove:' ' }}" and then the preceding code creates the actual named anchor div which displays the post associated with that category. Finally, under the page or section where you want to display the list of categories, you present the final bit of code which links to the named anchor section in your categories.html page.
First piece of code to go into Categories.html:
<h2>Posts by Category</h2>
<ul>
{% for category in site.categories %}
<li><strong>{{ category | first }}</strong></li>
{% if forloop.last %}
{% else %}
{% endif %}
{% endfor %}
</ul>
Second piece of code to go into Categories.html:
{% for category in site.categories %}
<div class="catbloc" id="{{ category | first | remove:' ' }}">
<h2>{{ category | first }}</h2>
<ul>
{% for posts in category %}
{% for post in posts %}
{% if post.url %}
<li>
<a href="{{ post.url }}">
<time>{{ post.date | date: "%B %d, %Y" }}</time> -
{{ post.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
</div>
{% endfor %}
Third piece of code to go where you want to display your named anchor linked categories:
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }}
{% endfor %}
{% endunless %}
Use the following CSS to prevent the sections from displaying prematurely before you click on them:
.catbloc:not(:target){
display: none;
}
Hope this helps!

Related

How to list tags from both posts and collections

I have a tags.md which contain the code below, It list the tags from my _site/post.md
---
layout: default
title: Tags
---
{% for tag in site.tags %}
<h2>{{ tag[0] }}</h2>
<ul>
{% for post in tag[1] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
And I also have a Collections for work stuff, like work.md in the jekyll root directory and the post in there is inside _work/
How can I make the tags.md list the tags from both _posts/*.md and _work/*.md?
I realize I can't do something like this
{% for tag in site.tags %}
{% for tag in work.tags %}
<h2>{{ tag[0] }}</h2>
<ul>
{% for post in tag[1] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endfor %}
Using site.documents should be your way to go, see https://jekyllrb.com/docs/variables/
site.documents
A list of all the documents in every collection.
Internally, posts are also treated as a collection here.
The first line below groups all documents by tag:
{% assign docs_by_tags = site.documents | group_by: 'tags' %}
{% for tag in docs_by_tags %}
<h2>{{ tag.name }}</h2>
<ul>
{% for item in tag.items %}
<li>{{ item.title }}</li>
{% endfor %}
</ul>
{% endfor %}
BUT the result shows quoted tag names, which is not what we want.
A better solution to group arrays
Someone else already found a good way to group arrays in posts and collections back in 2015, see https://github.com/mushishi78/jekyll-group-by-array/blob/master/group-by-array.html - the project's readme has some sample code using the include file.
The code "extracts" tags or other attributes and pushes data to arrays before data is returned to be displayed.
The solution with both post and collection tag
Just use site.documents in the include as first parameter
{% comment %}based on https://github.com/mushishi78/jekyll-group-by-array{% endcomment %}
{% include group-by-array.html collection=site.documents field='tags' %}
<ul>
{% for tag in group_names %}
{% assign posts = group_items[forloop.index0] %}
<li>
<h2>{{ tag }}</h2>
<ul>
{% for post in posts %}
<li>
<a href='{{ site.baseurl }}{{ post.url }}'>{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Reversing the posts order in the list
To reverse the post order under the tag, you can add a reversed to the for loop above:
{% for post in posts reversed %}

Display language filtered posts by category in Jekyll

I run a Jekyll blog in multiple languages using the setup making Jekyll multilingual by Sylvain Durand without use of any plugin.
All posts have the following markup:
---
title: Hello world!
lang: en
ref: hello
---
The posts are using the normal folder structure:
jekyll
|
-- posts
|
--name-of-post
--name-of-post-2
--name-of-post-3
I have a page named en.md which have layout: home and lang: en markup, which displays English posts correctly with the following code in home.html
{% assign posts=site.posts | where:"lang", page.lang %}
<ul>
{% for post in posts %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
But I would like instead to display posts by category, filtered on the language.
Tried to achieve this with the following:
{% assign posts=site.categories | where:"lang", page.lang %}
<div class="categories">
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
</div>
When I build, the following message is displayed
Liquid Exception: no implicit conversion of String into Integer in /_layouts/home.html
Tried many variants, but none seems to work.
This does the trick :
---
Title: English posts
lang: en
---
<ul>
{% for category in site.categories %}
{% comment %}
Here we have something like this
category = Array[
"category1",
[doc1, doc2]
]
{% endcomment %}
{% assign name = category[0] %}
{% assign posts = category[1] %}
{% comment %}
>> This also works
{% assign name = category.first %}
{% assign posts = category.last %}
{% endcomment %}
{% comment %}
>> Filtering posts based on their `lang` variable
>> and on the current `page.lang`
{% endcomment %}
{% assign selectedPosts = posts | where:"lang", page.lang %}
{% comment %}
>> Let's make sure that we need to print something
{% endcomment %}
{% if selectedPosts.size > 0 %}
<li>
Category {{ name }} :
<ul>
{% for post in selectedPosts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
Short version :
<ul class="post-list">
{% for c in site.categories %}
{% assign selectedPosts = c.last | where:"lang", page.lang %}
{% if selectedPosts.size > 0 %}
<li>Category {{ c.first }} :
<ul>
{% for post in selectedPosts %}
<li>{{ post.title }} - {{ post.lang }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
WITH the built-in category solution of Jekyll
I found this partial solution, just like you did...:
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
What you want to do/did is filter the 'posts' variable with the page language. This can indeed be done with assign using a where filter, but should look like this (as 'lang' is a attribute of the individual post and not of the category):
{% assign lang_posts = posts | where:"lang", page.lang %}
This leads to the following code:
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% assign lang_posts = posts | where:"lang", page.lang %}
{% for post in lang_posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
WITHOUT the built-in category solution of Jekyll
If you have a random list of categories in your front matter, like this:
- categories:
- web
- css
- internet
... and your sites _config.yml contains a similar (but more complete) list, like this:
- categories:
- web
- css
- internet
- html5
- jekyll
You have a whole other problem. In that case you are NOT using the built-in category solution of Jekyll and this solution does not apply. A lot of the statements mean different things in that case, like this:
{% for category in site.categories %}
{{ category | first }}
It means you loop over all existing categories from the _config.yml, and category | first should then be category. In this situation you probably want something like this:
<ul class="categories">
{% for category in site.categories %}
<li><a name="{{ category }}">{{ category }}</a>
<ul>
{% assign posts=site.posts | where:"lang", page.lang %}
{% for post in posts %}
{% if post.categories contains category %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Note that this is just simple Jekyll/liquid array logic that overwrites the Jekyll category variables.

Jekyll Grouping and Sorting Collection by Category and Priority

I have a bunch of documentation pages in a collection, which all have a Category, which I am using a group_by filter on like so:
{% assign docs_by_category = site.documentation | group_by: "category" %}
Then I use these groupings to create a menu structure, where items for each category are listed under a header. This is all good.
However, my problem lies in, being able to sort, which category is shown first, so I would like to be able to somehow prioritize them.
Say, if I have a the following categories: tutorials, getting started, advanced. I wouldn't want advanced to show up as the first category, but rather getting started.
The current code for generating the menu looks something like:
{% assign navurl = page.url | remove: 'index.html' %}
{% assign docs_by_category = site.documentation | group_by: "category" | reverse %}
{% for category in docs_by_category %}
<div class="category_wrapper">
<div class="category">{{ category.name }}</div>
<ul>
{% for item in category.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
TL;DR: How do I sort groups by some kind of priority?
You can use a configuration array to store categories order :
# _config.yml
categories-order:
- getting started
- tutorials
- advanced
Then :
{% assign docs_by_category = site.documentation | group_by: "category" %}
{% for cat in site.categories-order %}
{% assign currentCat = docs_by_category | where: 'name', cat | first %}
<div class="category_wrapper">
<div class="category">{{ currentCat.name }}</div>
<ul>
{% for item in currentCat.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
This implies that you reference all you categories in you config, otherwise, they will not appear in this listing.

In Jekyll, how can I get all pages with a specific tag and limit the results to 3?

In Jekyll, I want to create a homepage showing various categories of pages. For each category, I only want to show 5 pages. How can limit the page results to just 5 per category?
Here's the code I currently have for one of the category sections:
<ul>
{% for page in site.pages %}
{% for tag in page.tags %}
{% if tag == "news" %}
<li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}
</a></li>
<div class="summary">{{page.summary}}</div>
{% endif %}
{% endfor %}
{% endfor %}
</ul>enter code here
If I add limit:5 in the for page loop, it only looks at 5 pages -- it doesn't look at 5 pages with those tags:
<ul>
{% for page in site.pages limit:5 %}
{% for tag in page.tags %}
...
If I add limit: 5 on the page.tags line, it doesn't do anything:
<ul>
{% for page in site.pages %}
{% for tag in page.tags limit:5 %}
...
I want to avoid having one category that has a lot of pages from taking up too much space. Any ideas?
You need to loop all pages, and count how many with the right category you already found:
{% assign counter = '0' %}
{% for page in site.pages %}
{% for tag in page.tags %}
{% if tag == "news" and counter < '5' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}
</a> ({{ counter }})</li>
<div class="summary">{{page.summary}}</div>
{% endif %}
{% endfor %}
{% endfor %}
(tested with site.posts instead of site.pages on my blog, because I don't have a Jekyll project where the pages have tags/categories)
The syntax to increase the variable looks a bit ugly, but apparently increasing numeric variables in Liquid isn't that easy (this was the first time I tried it myself).
You should look at group_by, which you can use with the assign tag.
<ul>
{% assign authors = site.posts | group_by: "author" %}
{% for author in authors %}
<li>{{ author.name }}<ul>
{% for page in author.items limit: 5 %}
<li>{{ page.title }}</li>
{% endfor %}
</ul></li>
{% endfor %}
</ul>

Jekyll, Liquid - Get all pages from category from page

I have a question in Jekyll Liquid.
I have layout, where I want to show pages from category. To show category I use page.categories variable. When I show in bracket {{page.categories}} is correct.
but I don't know, how to pass to loop?
{% for post in site.categories[page.categories] %}
<li>{{ post.title }}</li>
{% endfor %}
{% for post in site.categories[{{page.categories}}] %}
<li>{{ post.title }}</li>
{% endfor %}
Don't work.
If I passed explicite:
{% for post in site.categories['cat1'] %}
<li>{{ post.title }}</li>
{% endfor %}
It works.
I found another topic:
Jekyll site.categories.{{variable}}?
But it doesn't work.
page.categories is a list (see Page Variables), so you need to loop through it first and pass each category to the loop from your question:
{% for cat in page.categories %}
<h1>{{ cat }}</h1>
<ul>
{% for post in site.categories[cat] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
This will first display all posts for the page's first category in descending order, then all posts for the page's second category in descending order, and so on.
Thank you. It's work.
Also I can use this code (use first on element of array, because in my case I have only one category per page):
{% assign pcat = page.categories %}
<ul>
{% for post in site.categories[pcat.first] %}
<li {% if post.url == page.url %}class="active"{% endif %}>{{ post.title }}</li>
{% endfor %}
</ul>