17
votes

I'm using Jekyll on GitHub pages, and I want to have hierarchical categories like this:

  • animals -> mammals -> cats -> _posts -> housecat.md, tiger.md
  • animals -> mammals -> dogs -> _posts -> poodle.md, doberman.md
  • animals -> reptiles -> lizards -> _posts -> iguana.md, chameleon.md

I'd like users to be able to visit /animals and see a listing of every post from every category. But if they go to /animals/mammals, they'd only see mammals. If they go to /animals/mammals/cats, then they only see cats.

I know I can do this manually by putting an index.html file in every single directory and then looping through site.categories.mammals or site.categories.cats, for example.

But that seems a little bit too brute force, and I'm hoping there's a better way. If I want to change how I'm showing the listings, I'll have to change that in every single subcategory. I'll also have problems when subcategories share a name, like /ABC/XYZ/_posts/one.md and /DEF/XYZ/_posts/two.md.

I've tried to follow this article, which uses one main category.html page that loops through page.category:

{% for post in site.categories.[page.category] %}
  <h2><a href=""></a></h2>
  <p></p>
{% endfor %}

Then every index.html file uses this as its layout. That almost works, but it seems limited to one category, not multiple hierarchical categories.

Is there a less brute-force approach to creating listings for hierarchical categories?

2
Did you consider to write a generator plugin?rgmt
@wasthishelpful I'm honestly not sure what that means. I'm also using GitHub pages, so I'm limited by what they allow.Kevin Workman
You could write a plugin to your jekyll installation to generate the category pages (see the second example here). But indeed if you're using GitHub pages, the plugin won't be accepted once pushed on your repository. An alternative could be to generate your site locally and then push your site's static files instead of the jekyll sources (dixit the doc)rgmt
@wasthishelpful Thanks, but I'd really like to keep this completely compatible with GitHub Pages. If my choices are just doing it manually or being incompatible, I'll just do it manually.Kevin Workman
After a quick search on google, it seems to be a "usual" way to have 2 repositories on github in this case: 1 repo eventually private for the sources with plugins and the one with the generated pages. But if you prefer not hacking github limitations, I understand your positionrgmt

2 Answers

5
votes

page.categories is a list

https://stackoverflow.com/a/23927986

{% for cat in page.categories %}
  <h1>{{ cat }}</h1>
  <ul>
    {% for post in site.categories[cat] %}
      <li><a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endfor %}
  </ul>
{% endfor %}

From jekyll's documentation about page.category http://jekyllrb.com/docs/variables/#page-variables

The list of categories to which this post belongs. Categories are derived from the directory structure above the _posts directory. For example, a post at /work/code/_posts/2008-12-24-closures.md would have this field set to ['work', 'code']. These can also be specified in the YAML Front Matter.

You should be easily able to add a simple dynamic index.html to every folder and the categories should be hierarchical automatically.

Update

The above does NOT work. You need to treat the combination of categories of each hierarchy as a unique item. Concat the index page's categories, and compare that against all the posts in the site.

/foo/bar/_posts/2016-08-01-foo-bar-test.html

---
categories:
  - foo
  - bar
title: test foo bar
---

<h2>Foo Bar</h2>

/var/bar/_posts/2016-08-01-test-var-bar.html

---
categories:
  - var
  - bar
title: test var bar
---

<h2>Var Bar</h2>

/foo/bar/index.html

---
categories:
 - foo
 - bar
---

{% assign pagecat = page.categories | join ' ' | append: ' '%}
{% assign pagecatlen = page.categories.size %}
  <h1>{{ cat }}</h1>
  <ul>
    {% for post in site.posts %}
    {% assign postcat = '' %}
    {% for thispostcat in post.categories limit: pagecatlen %}
      {% assign postcat = postcat | append: thispostcat %}
      {% assign postcat = postcat | append: ' ' %}
    {% endfor %}

    {% if (postcat == pagecat) %}
      <li><a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endif %}
    {% endfor %}
  </ul>

The categories are optional for the files in _posts, but they are required for the front matter of each index file.

4
votes

Modify your _config.yml accordingly

collections:
    animals:
        output: true
        mammals:
            output: true
            cats:
                output: true
            dogs:
                output: true
        reptiles:
            output: true
            lizards:
                output: true

then created the structure:

mkdir -p _animals/reptiles/lizards
mkdir -p _animals/mammals/cats
mkdir _animals/mammals/dogs

add your md files and all index.html which will index items with filter. It should look like this (maybe with more indexes) :

_animals/
├── index.html
├── mammals
│   ├── cats
│   │   ├── housecat.md
│   │   └── tiger.md
│   ├── dogs
│   │   ├── doberman.md
│   │   └── poodle.md
│   └── index.html
└── reptiles
    └── lizards
        ├── chameleon.md
        └── iguana.md

then you create _includes/list_animals.html

{% assign animals = site.animals| sort:'title' %}
{% for animal in animals %}
{% if page.url != animal.url  and include.taxonomy == nil or animal.url contains include.taxonomy %}
<a  href={{ animal.url | prepend: site.baseurl }}>{{animal.title}}</a>
{% endif %}
{% endfor %} 

to list all animals in animals/index.html :

---
title: animals
---
{% include list_animals.html %}

For example, to list all mammals in animals/mammals/index.html :

---
title: animals
---
{% include list_animals.html taxonomy="mammals" %}

Finally the generated structure should look like this (with some more index.html):

_site
└── animals
    ├── index.html
    ├── mammals
    │   ├── cats
    │   │   ├── housecat.html
    │   │   └── tiger.html
    │   ├── dogs
    │   │   ├── doberman.html
    │   │   └── poodle.html
    │   └── index.html
    └── reptiles
        └── lizards
            ├── chameleon.html
            └── iguana.html