I am using github to host a static site and Jekyll to generate it.
I have a menu bar (as <ul>) and would like the <li> corresponding to the current page to be assigned a different class for CSS highlighting.
So something like pseudo code:
<li class={(hrefpage==currentpage)?"highlight":"nothighlight"} ...>
Or perhaps even generate the whole <ul> in Jekyll.
How can this be done with minimal changes outside of the offending <ul>?
Yes you can do this.
To accomplish this we will take advantage of the fact that the current page is always represented by the liquid variable: page in the template, and also that each post/page has a unique identifier in its page.url attribute.
This means that we just have to use a loop to build our navigation page, and by doing so we can check page.url against every member of the loop. If it finds a match, it knows to highlight that element. Here we go:
{% for node in site.pages %}
{% if page.url == node.url %}
<li class="active">{{node.title}}</li>
{% else %}
<li>{{node.title}}</li>
{% endif %}
{% endfor %}
This works as expected. However you probably don't want all your page's in your nav bar. In order to emulate page "grouping" you can something like this:
## Put the following code in a file in the _includes folder at: ./_includes/pages_list
{% for node in pages_list %}
{% if group == null or group == node.group %}
{% if page.url == node.url %}
<li class="active">{{node.title}}</li>
{% else %}
<li>{{node.title}}</li>
{% endif %}
{% endif %}
{% endfor %}
{% assign pages_list = nil %}
{% assign group = nil %}
Now pages can be "grouped". To give a page a group you need to specify it in the pages YAML Front Matter:
---
title: blah
categories: blah
group: "navigation"
---
Finally you can use your new code!
Wherever you need your navigation to go in your template, simply "call" your include file and pass it some pages and the group you want to display:
<ul>
{% assign pages_list = site.pages %}
{% assign group = 'navigation' %}
{% include pages_list %}
</ul>
Examples
This functionality is part of the Jekyll-Bootstrap framework.
You can see exact documentation for the code outlined here: http://jekyllbootstrap.com/api/bootstrap-api.html#jbpages_list
Finally you can see it in action within the website itself. Just look at the righthand navigation and you will see the current page is highlighted in green: Example highlighted nav link
I feel like for the simplest navigation requirement, the listed solutions are overly complex. Here's a solution that involves no front matter, javascript, loops, etc.
Since we have access to the page URL, we can normalize and split the URL and test against the segments, like so:
{% assign current = page.url | downcase | split: '/' %}
<nav>
<ul>
<li><a href='/about' {% if current[1] == 'about' %}class='current'{% endif %}>about</a></li>
<li><a href='/blog' {% if current[1] == 'blog' %}class='current'{% endif %}>blog</a></li>
<li><a href='/contact' {% if current[1] == 'contact' %}class='current'{% endif %}>contact</a></li>
</ul>
</nav>
Of course, this is only useful if static segments provide the means to delineate the navigation. Anything more complicated, and you'll have to use front matter like #RobertKenny demonstrated.
Here's my solution which I think is the best way to highlight the current page:
Define a navigation list on your _config.yml like this:
navigation:
- title: blog
url: /blog/
- title: about
url: /about/
- title: projects
url: /projects/
Then in your _includes/header.html file you must loop through the list to check if the current page (page.url) resembles any item of the navigation list, if so then you just set the active class and add it to the <a> tag:
<nav>
{% for item in site.navigation %}
{% assign class = nil %}
{% if page.url contains item.url %}
{% assign class = 'active' %}
{% endif %}
<a href="{{ item.url }}" class="{{ class }}">
{{ item.title }}
</a>
{% endfor %}
</nav>
And because you're using the contains operator instead of the equals = operator, you don't have to write extra code to make it work with URLs such as '/blog/post-name/' or 'projects/project-name/'. So it works really well.
P.S: Don't forget to set the permalink variable on your pages.
Similar to #ben-foster's solution but without using any jQuery
I needed something simple, this is what I did.
In my front matter I added a variable called active
e.g.
---
layout: generic
title: About
active: about
---
I have a navigation include with the following section
<ul class="nav navbar-nav">
{% if page.active == "home" %}
<li class="active">Home</li>
{% else %}
<li>Home</li>
{% endif %}
{% if page.active == "blog" %}
<li class="active">Blog</li>
{% else %}
<li>Blog</li>
{% endif %}
{% if page.active == "about" %}
<li class="active">About</li>
{% else %}
<li>About</li>
{% endif %}
</ul>
This works for me as the amount of links in the navigation are very narrow.
I used a little bit of JavaScript to accomplish this. I have the following structure in the menu:
<ul id="navlist">
<li><a id="index" href="index.html">Index</a></li>
<li>About</li>
<li>Projects</li>
<li>Videos</li>
</ul>
This javascript snippet highlights the current corresponding page:
$(document).ready(function() {
var pathname = window.location.pathname;
$("#navlist a").each(function(index) {
if (pathname.toUpperCase().indexOf($(this).text().toUpperCase()) != -1)
$(this).addClass("current");
});
if ($("a.current").length == 0)
$("a#index").addClass("current");
});
My approach is to define a custom variable in the YAML front matter of the page and output this on the <body> element:
<body{% if page.id %} data-current-page="{{ page.id }}"{% endif %}>
My navigation links include the identifier of the page that they link to:
<nav>
<ul>
<li>artists</li>
<li>contact</li>
<li>about</li>
</ul>
</nav>
In the page front matter we set the page id:
---
layout: default
title: Our artists
id: artists
---
And finally a bit of jQuery to set the active link:
// highlight current page
var currentPage = $("body").data("current-page");
if (currentPage) {
$("a[data-page-id='" + currentPage + "']").addClass("active");
}
The navigation of your website should be an unordered list. To get the list items to lighten up when they are active, the following liquid script adds an 'active' class to them. This class should be styled with CSS. To detect which link is active, the script uses ‘contains’, as you can see in the code below.
<ul>
<li {% if page.url contains '/getting-started' %}class="active"{% endif %}>
Getting started
</li>
...
...
...
</ul>
This code is compatible with all permalink styles in Jekyll. The ‘contains’ statement succesfully highlights the first menu item at the following URL’s:
getting-started/
getting-started.html
getting-started/index.html
getting-started/subpage/
getting-started/subpage.html
Source: http://jekyllcodex.org/without-plugin/simple-menu/
Lot's of confusing answers here.
I simply use an if:
{% if page.name == 'limbo-anim.md' %}active{% endif %}
I refer directly to the page and putting it inside the class I want to
<li><a class="pr-1 {% if page.name == 'limbo-anim.md' %}activo{% endif %} " href="limbo-anim.html">Animación</a></li>
Done. Quick.
I've been using page.path and going off the filename.
home
Lot of good answers are already there.
Try this.
I slightly alter the above answers.
_data/navigation.yaml
- name: Home
url: /
active: home
- name: Portfolio
url: /portfolio/
active: portfolio
- name: Blog
url: /blog/
active: blog
In a page -> portfolio.html (Same for all pages with a relative active page name)
---
layout: default
title: Portfolio
permalink: /portfolio/
active: portfolio
---
<div>
<h2>Portfolio</h2>
</div>
Navigation html part
<ul class="main-menu">
{% for item in site.data.navigation %}
<li class="main-menu-item">
{% if {{page.active}} == {{item.active}} %}
<a class="main-menu-link active" href="{{ item.url }}">{{ item.name }}</a>
{% else %}
<a class="main-menu-link" href="{{ item.url }}">{{ item.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
if you're using the Minima theme for jekyll, you only need to add a ternary on the class attribute in header.html:
<a {% if my_page.url == page.url %} class="active"{% endif %} href="{{ my_page.url | relative_url }}">
the full excerpt:
<div class="trigger">
{%- for path in page_paths -%}
{%- assign my_page = site.pages | where: "path", path | first -%}
{%- if my_page.title -%}
<a {% if my_page.url == page.url %} class="active"{% endif %} href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{%- endif -%}
{%- endfor -%}
</div>
add this to your _config.yml
header_pages:
- classes.markdown
- activities.markdown
- teachers.markdown
And then of course your css:
a.active {
color: #e6402a;
}
Here is a jQuery method to do the same
var pathname = window.location.pathname;
$(".menu.right a").each(function(index) {
if (pathname === $(this).attr('href') ) {
$(this).addClass("active");
}
});
Related
I am using github to host a static site and Jekyll to generate it.
I have a menu bar (as <ul>) and would like the <li> corresponding to the current page to be assigned a different class for CSS highlighting.
So something like pseudo code:
<li class={(hrefpage==currentpage)?"highlight":"nothighlight"} ...>
Or perhaps even generate the whole <ul> in Jekyll.
How can this be done with minimal changes outside of the offending <ul>?
Yes you can do this.
To accomplish this we will take advantage of the fact that the current page is always represented by the liquid variable: page in the template, and also that each post/page has a unique identifier in its page.url attribute.
This means that we just have to use a loop to build our navigation page, and by doing so we can check page.url against every member of the loop. If it finds a match, it knows to highlight that element. Here we go:
{% for node in site.pages %}
{% if page.url == node.url %}
<li class="active">{{node.title}}</li>
{% else %}
<li>{{node.title}}</li>
{% endif %}
{% endfor %}
This works as expected. However you probably don't want all your page's in your nav bar. In order to emulate page "grouping" you can something like this:
## Put the following code in a file in the _includes folder at: ./_includes/pages_list
{% for node in pages_list %}
{% if group == null or group == node.group %}
{% if page.url == node.url %}
<li class="active">{{node.title}}</li>
{% else %}
<li>{{node.title}}</li>
{% endif %}
{% endif %}
{% endfor %}
{% assign pages_list = nil %}
{% assign group = nil %}
Now pages can be "grouped". To give a page a group you need to specify it in the pages YAML Front Matter:
---
title: blah
categories: blah
group: "navigation"
---
Finally you can use your new code!
Wherever you need your navigation to go in your template, simply "call" your include file and pass it some pages and the group you want to display:
<ul>
{% assign pages_list = site.pages %}
{% assign group = 'navigation' %}
{% include pages_list %}
</ul>
Examples
This functionality is part of the Jekyll-Bootstrap framework.
You can see exact documentation for the code outlined here: http://jekyllbootstrap.com/api/bootstrap-api.html#jbpages_list
Finally you can see it in action within the website itself. Just look at the righthand navigation and you will see the current page is highlighted in green: Example highlighted nav link
I feel like for the simplest navigation requirement, the listed solutions are overly complex. Here's a solution that involves no front matter, javascript, loops, etc.
Since we have access to the page URL, we can normalize and split the URL and test against the segments, like so:
{% assign current = page.url | downcase | split: '/' %}
<nav>
<ul>
<li><a href='/about' {% if current[1] == 'about' %}class='current'{% endif %}>about</a></li>
<li><a href='/blog' {% if current[1] == 'blog' %}class='current'{% endif %}>blog</a></li>
<li><a href='/contact' {% if current[1] == 'contact' %}class='current'{% endif %}>contact</a></li>
</ul>
</nav>
Of course, this is only useful if static segments provide the means to delineate the navigation. Anything more complicated, and you'll have to use front matter like #RobertKenny demonstrated.
Here's my solution which I think is the best way to highlight the current page:
Define a navigation list on your _config.yml like this:
navigation:
- title: blog
url: /blog/
- title: about
url: /about/
- title: projects
url: /projects/
Then in your _includes/header.html file you must loop through the list to check if the current page (page.url) resembles any item of the navigation list, if so then you just set the active class and add it to the <a> tag:
<nav>
{% for item in site.navigation %}
{% assign class = nil %}
{% if page.url contains item.url %}
{% assign class = 'active' %}
{% endif %}
<a href="{{ item.url }}" class="{{ class }}">
{{ item.title }}
</a>
{% endfor %}
</nav>
And because you're using the contains operator instead of the equals = operator, you don't have to write extra code to make it work with URLs such as '/blog/post-name/' or 'projects/project-name/'. So it works really well.
P.S: Don't forget to set the permalink variable on your pages.
Similar to #ben-foster's solution but without using any jQuery
I needed something simple, this is what I did.
In my front matter I added a variable called active
e.g.
---
layout: generic
title: About
active: about
---
I have a navigation include with the following section
<ul class="nav navbar-nav">
{% if page.active == "home" %}
<li class="active">Home</li>
{% else %}
<li>Home</li>
{% endif %}
{% if page.active == "blog" %}
<li class="active">Blog</li>
{% else %}
<li>Blog</li>
{% endif %}
{% if page.active == "about" %}
<li class="active">About</li>
{% else %}
<li>About</li>
{% endif %}
</ul>
This works for me as the amount of links in the navigation are very narrow.
I used a little bit of JavaScript to accomplish this. I have the following structure in the menu:
<ul id="navlist">
<li><a id="index" href="index.html">Index</a></li>
<li>About</li>
<li>Projects</li>
<li>Videos</li>
</ul>
This javascript snippet highlights the current corresponding page:
$(document).ready(function() {
var pathname = window.location.pathname;
$("#navlist a").each(function(index) {
if (pathname.toUpperCase().indexOf($(this).text().toUpperCase()) != -1)
$(this).addClass("current");
});
if ($("a.current").length == 0)
$("a#index").addClass("current");
});
My approach is to define a custom variable in the YAML front matter of the page and output this on the <body> element:
<body{% if page.id %} data-current-page="{{ page.id }}"{% endif %}>
My navigation links include the identifier of the page that they link to:
<nav>
<ul>
<li>artists</li>
<li>contact</li>
<li>about</li>
</ul>
</nav>
In the page front matter we set the page id:
---
layout: default
title: Our artists
id: artists
---
And finally a bit of jQuery to set the active link:
// highlight current page
var currentPage = $("body").data("current-page");
if (currentPage) {
$("a[data-page-id='" + currentPage + "']").addClass("active");
}
The navigation of your website should be an unordered list. To get the list items to lighten up when they are active, the following liquid script adds an 'active' class to them. This class should be styled with CSS. To detect which link is active, the script uses ‘contains’, as you can see in the code below.
<ul>
<li {% if page.url contains '/getting-started' %}class="active"{% endif %}>
Getting started
</li>
...
...
...
</ul>
This code is compatible with all permalink styles in Jekyll. The ‘contains’ statement succesfully highlights the first menu item at the following URL’s:
getting-started/
getting-started.html
getting-started/index.html
getting-started/subpage/
getting-started/subpage.html
Source: http://jekyllcodex.org/without-plugin/simple-menu/
Lot's of confusing answers here.
I simply use an if:
{% if page.name == 'limbo-anim.md' %}active{% endif %}
I refer directly to the page and putting it inside the class I want to
<li><a class="pr-1 {% if page.name == 'limbo-anim.md' %}activo{% endif %} " href="limbo-anim.html">Animación</a></li>
Done. Quick.
I've been using page.path and going off the filename.
home
Lot of good answers are already there.
Try this.
I slightly alter the above answers.
_data/navigation.yaml
- name: Home
url: /
active: home
- name: Portfolio
url: /portfolio/
active: portfolio
- name: Blog
url: /blog/
active: blog
In a page -> portfolio.html (Same for all pages with a relative active page name)
---
layout: default
title: Portfolio
permalink: /portfolio/
active: portfolio
---
<div>
<h2>Portfolio</h2>
</div>
Navigation html part
<ul class="main-menu">
{% for item in site.data.navigation %}
<li class="main-menu-item">
{% if {{page.active}} == {{item.active}} %}
<a class="main-menu-link active" href="{{ item.url }}">{{ item.name }}</a>
{% else %}
<a class="main-menu-link" href="{{ item.url }}">{{ item.name }}</a>
{% endif %}
</li>
{% endfor %}
</ul>
if you're using the Minima theme for jekyll, you only need to add a ternary on the class attribute in header.html:
<a {% if my_page.url == page.url %} class="active"{% endif %} href="{{ my_page.url | relative_url }}">
the full excerpt:
<div class="trigger">
{%- for path in page_paths -%}
{%- assign my_page = site.pages | where: "path", path | first -%}
{%- if my_page.title -%}
<a {% if my_page.url == page.url %} class="active"{% endif %} href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{%- endif -%}
{%- endfor -%}
</div>
add this to your _config.yml
header_pages:
- classes.markdown
- activities.markdown
- teachers.markdown
And then of course your css:
a.active {
color: #e6402a;
}
Here is a jQuery method to do the same
var pathname = window.location.pathname;
$(".menu.right a").each(function(index) {
if (pathname === $(this).attr('href') ) {
$(this).addClass("active");
}
});
I'm building a static website on gh-pages using jekyll liquid. I'm properly generating a simple, two-level navigation from a data file. My problem is that I am stuck trying to do two things:
Apply a "selected" class to the current page link item.
Apply an "open" class to the parent list item when a sublink menu is present.
Here's the format I'm using in my _data/nav.yml file
- title: Top Level Nav Item
url: level-1/
sublinks:
- title: Child Nav Item 1
url: child-1/
- title: Child Nav Item 2
url: child-2/
Here's how I'm building my navigation:
{% assign current_page = page.url | remove: 'index.html' %}
<ul class="-nav">
{% for nav in include.nav %}
{% assign current = null %}
{% if nav.url == current_page %}
{% assign current = ' _selected' %}
{% endif %}
{% if nav.url contains current_page %}
{% assign open = ' _open' %}
{% endif %}
<li class="-item{{ current }}{{ open }}">
{{ nav.title }}
{% if nav.sublinks and current_page contains nav.url %}
{% include navigation.html nav=nav.sublinks%}
{% endif %}
</li>
{% endfor %}
</ul>
Again this builds my navigation correctly, but it doesn't apply either the selected or open class.
Here's what I'm would like it to look like in the end:
What am I doing wrong?
Finally got this working. I solved it by adding an item to the front matter of my page called permalink where I specified the desired page permalink.
---
layout: default
title: Example
permalink: url/example/
---
I use this permalink to check against the current page.url to add the desired _selected class on the <li> element.
I made one modification to my example data file, adding in the parent url_part into the child's url. I'm not sure why, but I had trouble printing the entire URL correctly otherwise.
- title: Top Level Nav Item
url: level-1/
sublinks:
- title: Child Nav Item 1
url: level-1/child-1/
- title: Child Nav Item 2
url: level-1/child-2/
Lastly for my navigation.html include, here's how I'm creating my main menu, rendering sub-menus if they exist and should be shown, and properly selecting the active link:
{% assign current_page = page.url | remove: 'index.html' %}
<ul class="-nav">
{% for nav in include.nav %}
{% assign current = null %}
{% if nav.url == page.permalink %}
{% assign current = ' _selected' %}
{% endif %}
<li class="-item{{ current }}">
{{ nav.title }}
{% if nav.sublinks and current_page contains nav.url %}
{% include navigation.html nav=nav.sublinks%}
{% endif %}
</li>
{% endfor %}
</ul>
The big difference between this and the snippet I originally posted is I dropped the {{ open }} stuff for now. One problem at a time. The other thing is that I'm checking to see if nav.url equals page.permalink. Before I was checking against page.url and this always failed for me.
It's probably not the prettiest, but I finally got a jekyll liquid menu to generate (semi-)dynamically and properly select the active link.
My blog is built with Jekyll on Github. In the navigation bar, the default order is Pages, Messages, About, Archives. I want to change the list to Pages, Archives, About, Messages. What should I do?
I think it is related to the code below
{% assign pages_list = site.pages %}
I think site.pages is what I should change, but I don't know how.
You can create custom order of your menu items like this:
In your pages front matter add the order field (you can name it as you prefer)
---
layout: default
published: true
title: Page title
order: 1
---
When getting pages, apply the 'sort' filter
{% assign sorted_pages = site.pages | sort:"order" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
You'll end up with an ordered (ASC) list of pages, based on the 'order' field value you add to each page.
Update: Some ordering functionality seems to have been added to Jekyll: https://github.com/plusjade/jekyll-bootstrap/commit/4eebb4462c24de612612d6f4794b1aaaa08dfad4
Update: check out comment by Andy Jackson below – "name" might need to be changed to "path".
This seems to work for me:
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
name is file name. I renamed pages to 00-index.md, 01-about.md etc. Sorting worked, but pages were generated with those prefixes, which looked ugly especially for 00-index.html.
To fix that I override permalinks:
---
layout: default
title: News
permalink: "index.html"
---
Sadly, this won't work with custom attributes, because they are not accessible as methods on Page class:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
The order of your navbar menu is determined by the HTML template in _layout (which may be pulling in HTML fragments from _includes.
It sounds like your navbar is being programatically generated from the list of pages provided in site.pages using the liquid code
{% assign pages_list = site.pages %}
If you have only a small number of pages, you may prefer to just write the list out manually. site.pages is Jekyll's alphabetical list of all pages. Nothing stops you from just hardcoding this instead:
<div class="navbar" id="page-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="/">EverCoding.net</a>
<ul class="nav">
<li>Pages</li>
<li>Archive</li>
<li>About</li>
<li>Messages</li>
Whereas I'm guessing at the moment you have that list generated programmatically, perhaps by following the way Jekyll-bootstrap does with liquid code:
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="{{ HOME_PATH }}">{{ site.title }}</a>
<ul class="nav">
{% assign pages_list = site.pages %}
{% assign group = 'navigation' %}
{% include JB/pages_list %}
</ul>
</div>
</div>
</div>
The liquid code in this second example is handy if you really want to determine the menu each time, but if you have a static menu in a static order you are probably best coding it by hand as in my first example, rather than modifying the liquid code to sort.
If you could link to the Jekyll source, rather than the published blog, we could be more specific.
I'm using Jekyll v2.5.3 and you can also number your actual markdown files (order them that way) and since you're using the Front Matter block you can still keep the titles and permalinks as you want them.
The parser will order your page links that way.
I.e.:
01_about.md
02_photos.md
03_projects.md
99_contact.md
I made pages.yml file in the _data directory it is look like similar:
- url: pages/test.html
title: Pages
group: navigation
- url: pages/front.html
title: Front
group: navigation
And I changed the default.html (from site.pages to site.data.pages):
<ul class="nav">
{% assign pages_list = site.data.pages %}
{% assign group = 'navigation' %}
{% include JB/pages_list %}
</ul>
And now I can use this yml file for the menu.
You could see the documentation: http://jekyll.tips/jekyll-casts/navigation/
There are good examples and explanations with navigation_weight.
---
layout: page
title: About
permalink: /about/
navigation_weight: 10
---
For minima:
<div>
{% assign navigation_pages = site.pages | sort: 'navigation_weight' %}
{% for p in navigation_pages %}
{% if p.navigation_weight %}
{% if p.title %}
<a class="page-link" href="{{ p.url | relative_url }}">{{ p.title | escape }}</a>
{% endif %}
{% endif %}
{% endfor %}
</div>
For minima theme
Put:
header_pages:
- pages.md
- archive.md
- about.md
- messages.md
in _config.yml to override default order. That's all.
Minima README:
Customize navigation links
This allows you to set which pages you want to appear in the
navigation area and configure order of the links.
For instance, to only link to the about and the portfolio page,
add the following to you _config.yml:
- about.md
- portfolio.md
You can see how it works in header.html file from minima _includes.
You were on the right path. You could sort by a custom variable named, say, 'order'.
In header.html insert and extra row:
{% assign pages_list = (site.pages | sort: 'order') %}
Then replace site.pages with pages_list in the for statement:
{% for my_page in pages_list %}
{% if my_page.title %}
<a class="page-link" href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{% endif %}
{% endfor %}
Then add 'order' into the YAML front matter for each page, and set it a suitable value:
---
layout: page
title: About
permalink: /about/
order: 0
---
The Jekyll Bootstrap 3 template requires that you include group navigation in the Jekyll header. Building on #Wojtek's answer, you can modify JB3's pages_list to use this group field to both filter, and sort.
Before calling pages_list, sort by group:
{% assign sorted_pages = site.pages | sort:"group" %}
Then, simply change one line in pages_list:
{% if group == null or group == node.group %} -> {% if group == null or node.group contains group %}
Now you can specify the group to be navigation-00, navigation-01, without having to rename your files or set up any permalinks, and you get sorting for free.
I made a simple plugin some time ago to sort pages according to a page_order array you can define your _config.yml:
pages_order: ['index', 'summary', 'overview', 'part1', 'part2', 'conclusion', 'notes']
It exposes page.prev and page.next in templates to allow navigation:
{% if page.prev %}
<a id="previous-page" href="{{page.prev}}.html">Previous</a>
{% endif %}
{% if page.next %}
<a id="next-page" href="{{page.next}}.html">Next</a>
{% endif %}
Note: Does not work on Github Pages.
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 page. The items should be added to the menu in a particular order. Therefore, I define a weight property in the pages' YAML:
---
layout : default
title : Some title
weight : 5
---
The navigation menu is constructed as follows:
<ul>
{% for p in site.pages | sort:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
This creates links to all existing pages, but they're unsorted, the sort filter seems to be ignored. Obviously, I'm doing something wrong, but I can't figure out what.
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.
Below solution works on Github (doesn't require a plugin):
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li>{{node.title}}</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'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)
Use tag 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.
Don't forget to use different end tag 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>
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
---
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">{{ p.title }}</span>
{% endfor %}
</nav>
</section>
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.
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 %}'>
{{ post.title }}
</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>{{page.title}}</li>
{% endif %}
{% endfor %}
{% endfor %}
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>{{ p.title }}</li>
{% endif %}
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>
I've been using Jekyll for a static site (so that its easy to maintain), and have been stuck at the following feature :
This is my link bar :
<ul id="links">
<li class="first"><a class="active" href="/">Home</a></li>
<li>Associate With Us</li>
<li>Media</li>
<li>Clients</li>
<li class="last">Contact Us</li>
</ul>
The active class handles the coloring. What I want is this class be applied by jekyll depending on some variable set using liquid/YAML.
Is there some easy way to go about this?
Since the bar is common to all the pages, it is now in the default layout. I could go around by using Javascript to detect the url, and do the highlighting but was wondering if there was any way of doing this in Jekyll.
I do this in two pages I have set up in Jekyll.
The first thing I do is creating an entry inside _config.yml with the information of all the pages:
# this goes inside _config.yml. Change as required
navigation:
- text: What we do
url: /en/what-we-do/
- text: Who we are
url: /en/who-we-are/
- text: Projects
url: /en/projects/
layout: project
- text: Blog
url: /en/blog/
layout: post
Then, on my main layout, I use that information to generate the navigation links. On each link, I compare the url of the link with the url of the current page. If they are equal, the page is active. Otherwise, they are not.
There's a couple special cases: all blog posts must highlight the "blog" link, and the front pages (English and Spanish) must not present the nav bar. For both cases, I rely on the fact that blog posts and front pages have specific layouts (notice that the "Blog" and "Project" links on the yaml have an extra parameter called "layout")
The navigation code is generated like this:
{% unless page.layout == 'front' %}
<ul class="navigation">
{% for link in site.navigation %}
{% assign current = nil %}
{% if page.url == link.url or page.layout == link.layout %}
{% assign current = 'current' %}
{% endif %}
<li class="{% if forloop.first %}first{% endif %} {{ current }} {% if forloop.last %}last{% endif %}">
<a class="{{ current }}" href="{{ link.url }}">{{ link.text }}</a>
</li>
{% endfor %}
</ul>
{% endunless %}
I still have to remember adding an entry to _config.yaml every time I add a new page, and then restart Jekyll, but it happens very infrequently now.
I think the navigation yaml could go inside an _include called "navigation" or something similar, but I haven't tried using yaml inside those so I don't know whether it will work. In my case, since I've got a multi-lingual site, it's easier to have everything inside config (missing translations are easier to spot)
I hope this helps.
As a further extension on the work of the other, here is a way to make it work without soggy index.html showing on all your beautiful URLs:
---
navigation:
- text: Home
url: /
- text: Blah
url: /blah/
---
{% assign url = page.url|remove:'index.html' %}
{% for link in page.navigation %}
<li {% if url == link.url %}class="active"{% endif %}>
{{link.text}}
</li>
{% endfor %}
The gold is in the assign statement which gets the page URL (which naturally includes the index.html and then strips it off to match the page.navigation pretty URLs.
This may be a new feature since the question first appeared, but I've discovered this can all be done in one file:
define the navigation as a variable in the yaml header
loop over the variable using liquid
So in my _layouts/base.html I have:
---
navigation:
- text: Home
url: /index.html
- text: Travel
title: Letters home from abroad
url: /travel.html
---
<ul>
{% for link in page.navigation %}
<li {% if page.url == link.url %}class="current"{% endif %}>
{{link.text}}</li>
{% endfor %}
</ul>
Which generates this on the Home page:
<ul>
<li class="current">Home</li>
<li>Travel</li>
</ul>
And this on the Travel page:
<ul>
<li>Home</li>
<li class="current">Travel</li>
</ul>
I needed something simple, this is what I did.
In my front matter I added a variable called active
e.g.
---
layout: generic
title: About
active: about
---
I have a navigation include with the following section
<ul class="nav navbar-nav">
{% if page.active == "home" %}
<li class="active">Home</li>
{% else %}
<li>Home</li>
{% endif %}
{% if page.active == "blog" %}
<li class="active">Blog</li>
{% else %}
<li>Blog</li>
{% endif %}
{% if page.active == "about" %}
<li class="active">About</li>
{% else %}
<li>About</li>
{% endif %}
</ul>
This works for me as the amount of links in the navigation are very narrow.
Same navigation on all pages
After reading all that answers, I came up with a new and easier to maintain solution:
Add {% include nav.html %} to your _layouts/layout.html file
Add a nav.html file to your _includes folder
Add the following content to your nav.html file. It will determine if your link is on the current page and add a class of active as well as remove index.html from your link. It will also work with a subfolder like /repo-name, which is needed for GitHub gh-pages Pages branch.
<nav>
<ul class="nav">
{% assign url = page.url|remove:'index.html' %}
{% for link in site.navigation %}
{% assign state = '' %}
{% if page.url == link.url %}
{% assign state = 'active ' %}
{% endif %}
<li class="{{ state }}nav-item">
{{ link.text }}
</li>
{% endfor %}
</ul>
</nav>
Then add the nav link array to your _config.yml file as following. Mind the right indentation, as YAML is pretty picky on that (Jekyll as well).
navigation:
- text: Home
title: Back to home page
url: /index.html
- text: Intro
title: An introduction to the project
url: /intro.html
- text: etc.
title: ...
url: /foo.html
Navigation only on home - "back" for others
Assuming that the link to your "Home"-page (index.html) is the first array part inside the _config.htmls navigation array, you can use the following snippet to show the navigation to the different pages on the main/home page and a link back to the home page on all other pages:
<nav>
<ul class="grid">
{% assign url = page.url | remove:'/index.html' %}
{% assign home = site.navigation.first %}
{% if url == empty %}
{% for link in site.navigation %}
{% assign state = '' %}
{% if page.url == link.url %}
{% assign state = 'active ' %}
{% endif %}
{% if home.url != link.url %}
<li class="{{ state }}nav-item">
{{ link.text }}
</li>
{% endif %}
{% endfor %}
{% else %}
<li class="nav-item active">
{{ home.text }}
</li>
{% endif %}
</ul>
</nav>
This works for me (sorry for non-english code):
<nav>
<a href="/kursevi" {% if page.url == '/kursevi/' %} class="active"{% endif %}>Kursevi</a>
<a href="/radovi" {% if page.url == '/radovi/' %} class="active"{% endif %}>Radovi</a>
<a href="/blog" {% if page.url == '/blog/' %} class="active"{% endif %}>Blog</a>
<a href="/kontakt" {% if page.url == '/kontakt/' %} class="active"{% endif %}>Kontakt</a>
</nav>
Navigation highlighting logic is the last thing I would do in server side. I'd rather stick with nice, clean and unobtrusive approach using JS/jQuery (if this option works for you).
The elements which need to be highlighted go inside a common layout like this:
<nav>
<a id="section1" href="#">Section 1</a>
<a id="section2" href="#">Section 2</a>
<a id="section3" href="#">Section 3</a>
</nav>
In your page (or even in nested layouts) you place an element with data-ref="#section1" (in fact, any jQuery selector will work).
Now, include following JS snippet (shown as jQuery, but any framework will do) in <head>:
$(function() {
$("[data-ref]").each(function() {
$($(this).attr("data-ref")).addClass("current");
});
});
This approach is nice, because it makes no assumptions on underlying architecture, frameworks, languages and template engines.
Update: As some folks pointed out you might want to omit the $(function() { ... }) part which binds the script to DOM ready event — and just push the script down to the bottom of your <body>.
Adding to the solution of incarnate, the simplest possible line of code with jQuery is this:
$( "a[href='{{ page.url | remove: "index.html" }}']" ).addClass("current");
Works perfectly for me.
I took a simple CSS approach. First, add an identifier in the front matter...
---
layout: page
title: Join us
permalink: /join-us/
nav-class: join <-----
---
Add this as a class in <body> and your nav...
<body class="{{ page.nav-class }}">
and
<li class="{{ page.nav-class }}">...</a></li>
Then link these for all pages in CSS, e.g:
.about .about a,
.join .join a,
.contact .contact a {
color: #fff;
}
The main downside being you need to hard code the style rules.
With Jekyll 3.4.3, this works :
<ul>
{% for my_page in site.pages %}
{% if my_page.title %}
<li {% if my_page.url == page.url %} class="active" {% endif %}>
<a href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}
</a>
</li>
{% endif %} {% endfor %}
</ul>