Excluding page from Jekyll navigation bar - jekyll

I am setting up a basic Github-hosted Jekyll website (so minimal, I am not even bothering to change the default theme). I have a nested site with a small number of first-tier pages that I would like to appear in the navigation bar (i.e. the default mode of operation). I also have some second-tier pages that I would like to NOT junk up the navigation bar.
While a multi-level navigation system would be nice, I'm trying to avoid using plugins. Therefore, I believe the simplest solution is to just exclude tier two pages from the navigation system entirely.
Here's a hypothetical page structure (minus the other Jekyll files):
jekyllsite
jekyllsite/bar
jekyllsite/bar/alice
jekyllsite/bar/alice/index.md
jekyllsite/bar/bob
jekyllsite/bar/bob/index.md
jekyllsite/bar/index.md
jekyllsite/baz
jekyllsite/baz/index.md
jekyllsite/foo
jekyllsite/foo/eggs
jekyllsite/foo/eggs/index.md
jekyllsite/foo/index.md
jekyllsite/foo/spam
jekyllsite/foo/spam/index.md
jekyllsite/index.md
In descending order of awesome, this is how I'd like this to go down:
Best case, context sensitive navigation (don't think possible without plugins): When visiting jekyllsite/index.md, I would get a single layer navigation bar offering me links to foo, bar, and baz. When visiting jekyllsite/bar/index.md, I would see a two-tiered navigation bar containing foo, bar, and baz at the top level, and with alice and bob in the second tier.
The next best option would be for me to change something globally, such that only top-level directories (foo, bar, baz) got added to the nav bar. Subdirectories such as alice, bob, spam, and eggs would be automatically excluded from the nav bar.
Finally (and I think this might be the easiest) would be for a YAML frontmatter flag to exclude a page. Something like nonav: true in the frontmatter of the page to be excluded.
This seems like it would have to be a fairly common use case, though I haven't been able to find anything that looks like a short path to either of these three options. I'm hoping someone more familiar with Jekyll has a "path of least resistance" answer.

I personally do ;
Front matter for page that appears in main menu
---
layout: default
title: Home
menu: main
weight: 10
---
Main menu template (classes are from twitter bootstrap) :
<ul class="nav navbar-nav">
{% comment %}Jekyll can now sort on custom page key{% endcomment %}
{% assign pages = site.pages | sort: 'weight' %}
{% for p in pages %}
{% if p.menu == 'main' %}
<li{% if p.url == page.url %} class="active"{% endif %}>
{{ p.title }}
</li>
{% endif %}
{% endfor %}
</ul>
You can then replicate that at any level by setting a custom var in the yaml front matter :
menu : foo
and passing a value to a menu template
{% include navbar.html menuLevel="foo" %}
And intercept it like this :
{% if p.menu == menuLevel %}
Any page that doesn't expose a menu: toto will not appear in navigation.

If you are coming from the basic Jekyll theme, the simplest way to exclude pages from the header site navigation is to add an unless page.exclude exception.
(page.exclude is a new Yaml frontmatter attribute.)
By default, this is in _includes/header.html:
{% for page in site.pages %}
{% unless page.exclude %}
{% if page.title %}
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{ page.title }}</a>
{% endif %}
{% endunless %}
{% endfor %}
and a corresponding tag to the Yaml frontmatter of any page:
---
... other attributes ...
exclude: true
---
Credit to Michael Chadwick.

It is possible to create a multi-level, context-sensitive navigation like you described without plugins, I have done it.
The only caveat is that you need to maintain a YAML data file with your menu hierarchy - with my approach, it's not possible to generate this automatically from your directory structure.
I'll show the short version here, but I have a way more detailed explanation on my blog:
Building a pseudo-dynamic tree menu with Jekyll
Example project on GitHub
1. Create a YAML data file (/_data/menu.yml) which contains your menu hierarchy:
- text: Home
url: /
- text: First menu
url: /first-menu/
subitems:
- text: First menu (sub)
url: /first-menu/first-menu-sub/
subitems:
- text: First menu (sub-sub)
url: /first-menu/first-menu-sub/first-menu-sub-sub/
- text: Second menu
url: /second-menu/
subitems:
- text: Second menu (sub)
url: /second-menu/second-menu-sub/
2. Create an include file (/_includes/nav.html) with the following content:
{% assign navurl = page.url | remove: 'index.html' %}
<ul>
{% for item in include.nav %}
<li>
<a href="{{ item.url }}">
{% if item.url == navurl %}
<b>{{ item.text }}</b>
{% else %}
{{ item.text }}
{% endif %}
</a>
</li>
{% if item.subitems and navurl contains item.url %}
{% include nav.html nav=item.subitems %}
{% endif %}
{% endfor %}
</ul>
This include file will take care of showing the correct navigation for each page:
showing the next level of subitems only for the current page
displaying the current page in bold
If you don't understand what exactly the include file is doing under the covers, read my blog post - I explained it there, in great detail (in the section "The recursive include file").
3. In your main layout file, embed the include file:
{% include nav.html nav=site.data.menu %}
This will display the navigation there.
Note that I'm passing the complete data file from step 1 to the include.
That's all!
As I said in the beginning:
The only disadvantage of this approach is that each time you create a new page, you also need to insert the page's title and URL into the data file.
But on the other hand, this makes it very easy to exclude some pages from the navigation: you just don't add them to the data file.

Related

Jekyll: Previous/Next navigation when using custom navigation

The official Jekyll tutorial has an entire section on using a YAML file to define a custom sequence of pages: https://jekyllrb.com/tutorials/navigation/
But it doesn't mention anywhere how one might create previous/next navigation buttons on the pages within that sequence, which is particularly ironic considering that the tutorial itself has them.
I've come up with some Liquid to determine the index of the current page:
{% for item in site.data.encore.docs %}
{% if item.url != page.url %}
{{ item.title }}: {{ forloop.index }}
{% else %}
<strong> This page index: {{ forloop.index }}</strong>
{% assign this_page_index = forloop.index %}
{% break %}
{% endif %}
{% endfor %}
but getting the index of the previous page via {% decrement this_page_index %} always returns -1 for some reason, and something like {% assign previous = this_page_index - 1 %} isn't valid Liquid. Same goes for trying to get the next page with similar methods.
What's the ideal way to accomplish this? I've searched every way I can think of and not found anything.
You can find the code for Jekyll's own navigation on their tutorials page by sifting through their GitHub repo until you get to their section_nav_tutorials.html, but it appears the way to do it is very close to what you have.
Liquid doesn't respect you doing math directly, you have to use a filter. For you, you'd use {% assign previous = this_page_index | minus: 1 %}.

Manually order a Jekyll collection

As mentioned in the Jekyll docs here, I have the following in my _config.yml as:
collections:
sections:
order:
- introduction.md
- battery-state.md
- vibe.md
- references.md
To render the content of each file within the HTML, I have the following:
{% for section in site.sections %}
{{ section.content }}
{% endfor %}
However, the content order is not presented as what I defined in the config file. How do I display the content in the order I defined in the config file?
Manually ordering documents in a collection was introduced in Jekyll 4.0
To use this feature, make sure that you're using Jekyll 4.0
For a site deployed on GitHub Pages, that would mean having to build the site outside GitHub Pages environment and upload the contents of the destination directory (_site).
You can also choose to add the sections to the front matter of the page. This is useful when you are not using Jekyll v4 or you want the user to be able to edit the order in CloudCannon, Netlify CMS, Forestry or another CMS with front matter editor.
sections:
- introduction
- battery-state
- vibe
- references
And the use a layout like this:
{% for s in page.sections %}
{% for section in site.sections %}
{% if s == section.slug %}
...
{% endif %}
{% endfor %}
{% endfor %}

Jekyll: Include record in site.records but do not render html page

I have a Jekyll setup that looks like this:
_config.yml
_records
a.html
b.html
c.html
...
I want to create a home page that links to each record. However, I want to render a.html and b.html to /records/, but I don't want to render c.html to /records/, as that HTML will be provided to my server from a different process altogether.
I tried setting the following in _config.yml:
exclude:
_records/c.html
But this also removes c.html from site.records, which is not what I want. The best solution I have right now is to prevent my deploy script from deploying _site/records/c.html, but I'd much rather prevent _site/records/c.html from being generated in the first place.
Is it possible to include c.html in site.records to create the links on the home page but not render /records/c.html? Any help others can offer with this question would be greatly appreciated!
Here's how I did this. Inside _records/c.html, set in the front matter:
permalink: '_'
route: /records/c.html
That will make it so that we render the page's html content to _site/_.html, a route that won't ever get visited.
Then in index.html to create the link to the route attribute of this page, use:
{% for record in site.records %}
{% if record.route %}
{% assign url = record.route %}
{% else %}
{% assign url = record.url %}
{% endif %}
<a href='{{ url }}'>{{ record.title }}</a>
{% endfor %}

how to create a dynamic menu in Jekyll that auto-populates nav items when creating new pages

I am wondering how to go about creating a dynamic menu in Jekyll that automatically populates the navigation with nav items whenever a new page is created. I read an article that touched a bit on the subject, but it was geared toward just sub-nav items. Has anyone had any experience in doing something like this?
Thanks!
It is a bad idea to do this automatically. However, it is very easy to achieve. Here is the code:
<ul>
{% for item in site.pages %}
<li {% if page.url contains item.url %}class="active"{% endif %}>
{{ item.title }}
</li>
{% endfor %}
</ul>
Source: https://jekyllcodex.org/without-plugin/simple-menu/
To define the order of appearence, you might want to add a front matter variable called 'order' to the pages and add a different pagenumber to this variable on each page. The code should sort the pages before looping over them. That looks like this:
{% assign sitepages = site.pages | sort: order %}
{% for item in site.pages %}
...
{% endfor %}
Happy coding!

Hide a page in Jekyll website served by GitHub

How do I hide a page in Jekyll? I have a Contact Us page (as a Google Docs form), and there is a response page. When created, it shows up in the navigation as a child of the Contact Us page, but I don't want it to show up at all.
I currently have this set up in the front matter like this:
---
layout: page
title: Thanks
permalink: /contact/thanks/
---
If you do not put any title in the page, it does not show up in the nav bar. Something like
---
layout: page
permalink: /contact/thanks/
---
Rather than 'opting out' of including certain pages, you can 'opt in' to include only the pages you do want included in your navigation. This is useful if you have a large number of pages.
Include a menu variable in the front matter of every page you do want included in the navigation:
---
layout: blog
title: Blog
menu: main
permalink: /blog/
---
And then add an if statement where your navigation is generated:
<ul>
{% for page in site.pages %}
{% if page.menu == 'main' %}
<li>{{ page.title }}</li>
{% endif %}
{% endfor %}
</ul>
This is likely to be in _layouts/default.html or possibly _includes/header.html.
Thanks to David Jaquel's answer on this question for the inspiration: Excluding page from Jekyll navigation bar
Just add a show_in_nav: false in you page front matter and in your navigation bar, do a :
<ul>
{% for p in pages %}
{% unless show_in_nav == false %}
<li>{{ p.title }}</li>
{% endunless %}
{% endfor %}
</ul>
This will prevent your page from appearing in navigation bar.
According to docs, set published to false in your front matter:
---
layout: post
title: Blogging Like a Hacker
published: false
---
Front Matter