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
I've solved this using a generator. The generator iterates over pages, getting the navigation data, sorting it and pushing it back to the site config. From there Liquid can retrieve the data and display it. It also takes care of hiding and showing items.
Consider this page fragment:
---
navigation:
title: Page name
weight: 100
show: true
---
content.
The navigation is rendered with this Liquid fragment:
{% for p in site.navigation %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}
Put the following code in a file in your _plugins folder:
module Jekyll
class SiteNavigation < Jekyll::Generator
safe true
priority :lowest
def generate(site)
# First remove all invisible items (default: nil = show in nav)
sorted = []
site.pages.each do |page|
sorted << page if page.data["navigation"]["show"] != false
end
# Then sort em according to weight
sorted = sorted.sort{ |a,b| a.data["navigation"]["weight"] <=> b.data["navigation"]["weight"] }
# Debug info.
puts "Sorted resulting navigation: (use site.config['sorted_navigation']) "
sorted.each do |p|
puts p.inspect
end
# Access this in Liquid using: site.navigation
site.config["navigation"] = sorted
end
end
end
I've spent quite a while figuring this out since I'm quite new to Jekyll and Ruby, so it would be great if anyone can improve on this.
The solution above by @kikito also worked for me. I just added a few lines to remove pages without weight from the navigation and to get rid of white space:
<nav>
<ul>
{% for weight in (1..5) %}
{% unless p.weight %}
{% for p in site.pages %}
{% if p.weight == weight %}
{% if p.url == page.url %}
<li>{{ p.title }}</li>
{% else %}
<li><a href="{{ p.url }}" title="{{ p.title }}">{{ p.title }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>
Easy solution:
Assign a sorted array of site.pages
first then run a for loop on the array.
Your code will look like:
{% assign links = site.pages | sort: 'weight' %}
{% for p in links %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
This works in my navbar _include
which is simply:
<section id="navbar">
<nav>
{% assign tabs = site.pages | sort: 'weight' %}
{% for p in tabs %}
<span class="navitem"><a href="{{ p.url }}">{{ p.title }}</a></span>
{% endfor %}
</nav>
</section>
The simplest solution would be to prefix the filename of your pages with an index like this:
00-home.html 01-services.html 02-page3.html
Pages are be ordered by filename. However, now you'll have ugly urls.
In your yaml front matter sections you can override the generated url by setting the permalink variable.
For instance:
---
layout: default
permalink: index.html
---