Ordering links in jekyll - jekyll

I am trying to make a navigation bar that is order by an order tag in the yaml frontmatter of the site (the site is made with jekyll). What i want to achieve is something like this (with the order value of the page next to it)
[home1] [solutions2] [talent3] [contact4]
This is the solution i am playing with. Solutions has a dropdown box when hovered over, hence the weird if divide.
{% assign pages = site.pages | sort:"order" %}
{% for page in pages %}
{% if page.categories contains "index" and page.categories <> "solutions" %}
<li>{{page.title}}</li>
{% endif %}
{% if page.categories contains "index" and page.categories contains "solutions" %}
<li><div class= "dropdown">
{{page.title}}
<div class= "dropdown-content">
<p> Our Service Solutions are:</p>
<ul>
{% for subpage in site.pages %}
... truncated ...
{% endfor %}
</ul>
</div>
</div></li>
{% endif %}
{% endfor %}
with the frontmatter
---
order: 1
categories: [ 'index', 'home' ]
---
or in the case of solutions,
---
order: 2
categories: [ 'index', 'solutions' ]
---
(both truncated for clarity)
This, however, does not work. What happens is this:
[home1][talent3][contacts4][solutions2]
does anyone know what is happening? Thank you.

Related

Jekyll Liquid sub-navigation not properly selecting

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.

Having Jekyll categories count and linked to a list of posts related to it

I am working on a Jekyll project. This is a blog with posts which have categories.
I would like to succeed in having a sidebar on the right side listing the categories and the count of posts related to this category AND when clicking on the category itself, having the list of the posts related to this category.
For now, I succeeded in having the list of categories with their counts but not the display of the posts. Here is the code :
<ul class="tag-box inline">
{% assign tags_list = site.categories %}
{% if tags_list.first[0] == null %}
{% for tag in tags_list %}
{{ tag | capitalize }} <span>{{ site.tags[tag].size }}</span>
{% endfor %}
{% else %}
{% for tag in tags_list %}
<div>{{ tag[0] | capitalize }} <span> ({{ tag[1].size }}) </span></div>
{% endfor %}
{% endif %}
{% assign tags_list = nil %}
</ul>
I was looking for some solutions on Internet and some of them were explaining that I should create a folder category and under this folder as many folder as categories I have with an index.html to display the categories. However, this is a lot of duplicated content and I don't know if it is the best way to do.
Thank you for your help!
As you cannot use plugins, you will have to create one page for each category.
In order to avoid repeating content, you can factorize with help of layout chaining. Your category pages will use category_index.html as layout, that will itself use default.html.
Sidebar
<ul class="tag-box inline">
{% for category in site.categories %}
<div>{{ category[0] | capitalize }} <span> ({{ category[1].size }}) </span></div>
{% endfor %}
</ul>
If you want urls like category/toto/ you have to set permalink: pretty in _config.yml.
A category page : category/toto.html
---
layout: category_index
category: toto
---
That's all.
_layouts/category_index.html
---
layout: default
---
<div class="home">
<h1 class="page-heading">Posts in category : {{ page.category }}</h1>
<ul class="post-list">
{% for p in site.categories[page.category] %}
<li>
<h2>{{ p.title }}</h2>
</li>
{% endfor %}
</ul>
</div>

Accessing posts via directory tree in Jekyll

Let's say I have a directory like the following:
|things
|---|animals
|---|---|dog
|---|---|cat
|---|---|other
|---|languages
|---|---|Afrikaans
|---|---|Latin
|---|---|Japanese
|---|---|other
and I want to access all of the posts under the "other" category that is under the "languages" category.
I want to be able to do
{% for post in site.categories.things.languages.other do %}
but that apparently doesn't work in Jekyll.
If I can avoid it, I'd like to not do
{% for post in site.categories.cobol do %}
{% if post.categories equals ["things", "languages", "other"] %}
but if I absolutely must, I will.
If you want to simply output a list of posts contained in a given path :
languages_other.html
---
path: "things/languages/other/"
---
{% include post_by_folder.html path=page.path %}
_includes/posts_by_folder.html
<h1>Posts in folder {{ include.path }}</h1>
<ul>
{% for post in site.posts %}
{% if post.path contains include.path %}
<li>
{{ post.title }}
</li>
{%endif %}
{% endfor %}
</ul>
Now any time you want to make a list of posts in a particular folder, you just have to create a page like languages_other.html.

How to change the default order pages in Jekyll?

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.

Sorted navigation menu with Jekyll and Liquid

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>