I\'m constructing a static site (no blog) with Jekyll/Liquid. I want it to have an auto-generated navigation menu that lists all existing pages and highlight the current pag
Below solution works on Github (doesn't require a plugin):
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li><a href="{{node.url}}">{{node.title}}</a></li>
{% endfor %}
Above snippet sorts pages by file name (name
attribute on Page object is derived from file name). I renamed files to match my desired order: 00-index.md
, 01-about.md
– and presto! Pages are ordered.
One gotcha is that those number prefixes end up in the URLs, which looks awkward for most pages and is a real problem in with 00-index.html. Permalilnks to the rescue:
---
layout: default
title: News
permalink: "index.html"
---
P.S. I wanted to be clever and add custom attributes just for sorting. Unfortunately custom attributes are not accessible as methods on Page class and thus can't be used for sorting:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
I can get the code below works on with Jekyll/Liquid match to your requirement with category:
- creates links to all existing pages,
- sorted by weight (works as well on sorting per category),
- highlight the current page.
On top of them it shows also number of post. All is done without any plug-in.
<ul class="topics">
{% capture tags %}
{% for tag in site.categories %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<li class="topic-header"><b>{{ tag }} ({{ site.categories[tag] | size }} topics)</b>
<ul class='subnavlist'>
{% assign posts = site.categories[tag] | sort:"weight" %}
{% for post in posts %}
<li class='recipe {% if post.url == page.url %}active{% endif %}'>
<a href="/{{ site.github.project_title }}{{ post.url }}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Check it on action on our networking page. You may click a post to highlight the navigation, as well a given link to bring you to the source page where their weight is assigned.
If you're trying to sort by weight and by tag and limit the number to 10, here's code to do it:
{% assign counter = '0' %}
{% assign pages = site.pages | sort: "weight" %}
{% for page in pages %}
{% for tag in page.tags %}
{% if tag == "Getting Started" and counter < '9' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}</a></li>
{% endif %}
{% endfor %}
{% endfor %}
Since Jekyll 2.2.0 you can sort an array of objects by any object property. You can now do :
{% assign pages = site.pages | sort:"weight" %}
<ul>
{% for p in pages %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
And save a lot of build time compared to @kikito solution.
edit:
You MUST assign your sorting property as an integer weight: 10
and not as a string weight: "10"
.
Assigning sorting properties as string will ends up in a a string sort like "1, 10, 11, 2, 20, ..."
Your only option seems to be using a double loop.
<ul>
{% for weight in (1..10) %}
{% for p in site.pages %}
{% if p.weight == weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
Ugly as it is, it should work. If you also have pages without a weight, you will have to include an additional internal loop just doing {% unless p.weight %}
before/after the current internal one.
I've written a simple Jekyll plugin to solve this issue:
Copy sorted_for.rb
from https://gist.github.com/3765912 to _plugins
subdirectory of your Jekyll project:
module Jekyll
class SortedForTag < Liquid::For
def render(context)
sorted_collection = context[@collection_name].dup
sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] }
sorted_collection_name = "#{@collection_name}_sorted".sub('.', '_')
context[sorted_collection_name] = sorted_collection
@collection_name = sorted_collection_name
super
end
def end_tag
'endsorted_for'
end
end
end
Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
sorted_for
instead of for
with sort_by:property
parameter to sort by given property. You can also add reversed
just like the original for
.endsorted_for
.In your case the usage look like this:
<ul>
{% sorted_for p in site.pages sort_by:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endsorted_for %}
</ul>