How to determine active post category? - jekyll

I have a dropdown menu for categories and posts within them. Need to check if category is active - the opened post to use active class for menu category.
Posts can have new categories, they are not predefined.
Front matter is standard:
---
layout: post
title: "Some post title"
date: 2016-04-06 13:41:07 +0300
category: Events
---
In order to show class for open post in menu, I use a common technique
{% if page.url == post.url %}
<li class="active">{{post.title}}</li>
{% else %}
<li>{{post.title}}</li>
{% endif %}
How to apply class for a dropdown category in which open post is present?
Here's full code of categories with nested post titles for output, I currently have:
<ul class="nav navbar-nav">
Categories
<li class="dropdown active">
{% for category in site.categories %}
<li>{{ category | first }}
<ul class="dropdown-menu">
{% for posts in category %}
{% for post in posts %}
{% if page.url == post.url %}
<li class="active">{{post.title}}</li>
{% else %}
<li>{{post.title}}</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
</li>
</ul>

You can try this :
{% for category in site.categories %}
{% assign categoryName = category | first %}
<li>
<a href="#"
class="dropdown-toggle {% if page.category == categoryName %} active{% endif %}"
data-toggle="dropdown"
name="{{ categoryName }}">{{ categoryName }}</a>
...

Related

How to get "Shop" link active in main navigation in Shopify Debut theme?

This is in regards to the Shopify Debut theme!
So my main nav has two links, About us and Shop. When one clicks on Shop, one is directed to "https://myshop.com/collections". At this stage the Shop link gets the class site-nav--active and changes color to be visibly active.
My problem:
The moment I click on a collection I am directed to https://myshop.com/collections/collection-name, but the Shop link for some reason does not stay active. How can I change that? I need to the Shop link be active the moment the URL has collections in it, and also when on the cart page. Any ideas?
I think i need to change something in this block of code in the site-nav.liquid file.
{% comment %}
Renders a list of menu items
Accepts:
- linklist: {Object} Linklist Liquid object (required)
- wrapper_class: {String} CSS wrapper class for the navigation (optional)
Usage:
{% include 'site-nav', linklist: section.settings.main_linklist, wrapper_class: 'site-nav--centered' %}
{% endcomment %}
<ul class="site-nav list--inline{% if wrapper_class != blank %} {{ wrapper_class }}{% endif %}" id="SiteNav">
{% for link in linklists[linklist].links %}
{%- assign child_list_handle = link.title | handleize -%}
{% comment %}
Check if third-level nav exists on each parent link.
{% endcomment %}
{%- assign three_level_nav = false -%}
{% if link.links != blank %}
{% if link.levels == 2 %}
{%- assign three_level_nav = true -%}
{% endif %}
{% endif %}
{% if link.links != blank %}
<li class="site-nav--has-dropdown{% if three_level_nav %} site-nav--has-centered-dropdown{% endif %}{% if link.active %} site-nav--active{% endif %}" data-has-dropdowns>
<button class="site-nav__link site-nav__link--main site-nav__link--button{% if link.child_active %} site-nav__link--active{% endif %}" type="button" aria-expanded="false" aria-controls="SiteNavLabel-{{ child_list_handle }}">
<span class="site-nav__label">{{ link.title | escape }}</span>{% include 'icon-chevron-down' %}
</button>
<div class="site-nav__dropdown{% if three_level_nav %} site-nav__dropdown--centered{% endif %}" id="SiteNavLabel-{{ child_list_handle }}">
{% if three_level_nav %}
<div class="site-nav__childlist">
<ul class="site-nav__childlist-grid">
{% if link.links != blank %}
{% for childlink in link.links %}
<li class="site-nav__childlist-item">
<a href="{{ childlink.url }}"
class="site-nav__link site-nav__child-link site-nav__child-link--parent"
{% if childlink.current %} aria-current="page"{% endif %}
>
<span class="site-nav__label">{{ childlink.title | escape }}</span>
</a>
{% if childlink.links != blank %}
<ul>
{% for grandchildlink in childlink.links %}
<li>
<a href="{{ grandchildlink.url }}"
class="site-nav__link site-nav__child-link"
{% if grandchildlink.current %} aria-current="page"{% endif %}
>
<span class="site-nav__label">{{ grandchildlink.title | escape }}</span>
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
{% endif %}
</ul>
</div>
{% else %}
<ul>
{% for childlink in link.links %}
<li>
<a href="{{ childlink.url }}"
class="site-nav__link site-nav__child-link{% if forloop.last %} site-nav__link--last{% endif %}"
{% if childlink.current %} aria-current="page"{% endif %}
>
<span class="site-nav__label">{{ childlink.title | escape }}</span>
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</li>
{% else %}
<li {% if link.active %} class="site-nav--active"{% endif %}>
<a href="{{ link.url }}"
class="site-nav__link site-nav__link--main{% if link.active %} site-nav__link--active{% endif %}"
{% if link.current %} aria-current="page"{% endif %}>
<span class="site-nav__label">{{ link.title | escape }}</span>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
Below are two screenshot, which I hope will help you understand my problem.
Shop link active:
Shop link not active
The link.active
Returns true if the link object is active, or false if the link object is inactive.
So the current page URL needs to match the URL in that link.
In your case:
I need to the Shop link be active the moment the URL has collections in it, and also when on the cart page.
This should do the trick:
{%
if link.active or
request.page_type == "collection" or
request.page_type == "cart"
%}
{% assign link_active_class = "site-nav--active" %}
{% endif %}
<li class="{{ link_active_class }}">
<a href="{{ link.url }}"
class="site-nav__link site-nav__link--main{% if link.active %} site-nav__link--active{% endif %}"
{% if link.current %} aria-current="page"{% endif %}>
<span class="site-nav__label">{{ link.title | escape }}</span>
</a>
</li>
This code expand on what you had before, so if the exact "Shop" link is active OR the page type is a "collection" or "cart", the link is going to be active. If none of the conditions are met the link_active_class won't be defined and class will be empty class="".
Just make sure that this code is only applied to the "Shop" link otherwise all links will be active on collections or the cart page.
Add this code
{% assign link_active_class = "" %}
{% liquid
if link.active
assign link_active_class = "site-nav--active"
elsif link.title == "shop" and request.page_type == "collection"
assign link_active_class = "site-nav--active"
elsif link.title == "shop" and request.page_type == "cart"
assign link_active_class = "site-nav--active"
endif
%}
If link is active then it's business as usual, if not
We check if the link title is "shop" and the current page type is of the desired types then we add the active class.
Make sure that the link title case matches the case of the string we are checking against, in our case "shop", if it's "Shop", adjust the code, for the sake of simplicity I didn't want to add a filter to adjust the link.title case.
We should be able to merge the elsif lines into one line, however for the sake of readability and clarity I left them as two lines.
Above this line of code
<li class="site-nav--has-dropdown{% if three_level_nav %} site-nav--has-centered-dropdown{% endif %}{% if link.active %} site-nav--active{% endif %}" data-has-dropdowns>
Replace {% if link.active %} site-nav--active{% endif %} in the line above, with {{ link_active_class }}.
Now, it should look like this
<li class="site-nav--has-dropdown{% if three_level_nav %} site-nav--has-centered-dropdown{% endif %} {{ link_active_class }}" data-has-dropdowns>
I didn't test this however in theory it should do the trick!

Display language filtered posts by category in Jekyll

I run a Jekyll blog in multiple languages using the setup making Jekyll multilingual by Sylvain Durand without use of any plugin.
All posts have the following markup:
---
title: Hello world!
lang: en
ref: hello
---
The posts are using the normal folder structure:
jekyll
|
-- posts
|
--name-of-post
--name-of-post-2
--name-of-post-3
I have a page named en.md which have layout: home and lang: en markup, which displays English posts correctly with the following code in home.html
{% assign posts=site.posts | where:"lang", page.lang %}
<ul>
{% for post in posts %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
But I would like instead to display posts by category, filtered on the language.
Tried to achieve this with the following:
{% assign posts=site.categories | where:"lang", page.lang %}
<div class="categories">
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
</div>
When I build, the following message is displayed
Liquid Exception: no implicit conversion of String into Integer in /_layouts/home.html
Tried many variants, but none seems to work.
This does the trick :
---
Title: English posts
lang: en
---
<ul>
{% for category in site.categories %}
{% comment %}
Here we have something like this
category = Array[
"category1",
[doc1, doc2]
]
{% endcomment %}
{% assign name = category[0] %}
{% assign posts = category[1] %}
{% comment %}
>> This also works
{% assign name = category.first %}
{% assign posts = category.last %}
{% endcomment %}
{% comment %}
>> Filtering posts based on their `lang` variable
>> and on the current `page.lang`
{% endcomment %}
{% assign selectedPosts = posts | where:"lang", page.lang %}
{% comment %}
>> Let's make sure that we need to print something
{% endcomment %}
{% if selectedPosts.size > 0 %}
<li>
Category {{ name }} :
<ul>
{% for post in selectedPosts %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
Short version :
<ul class="post-list">
{% for c in site.categories %}
{% assign selectedPosts = c.last | where:"lang", page.lang %}
{% if selectedPosts.size > 0 %}
<li>Category {{ c.first }} :
<ul>
{% for post in selectedPosts %}
<li>{{ post.title }} - {{ post.lang }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
WITH the built-in category solution of Jekyll
I found this partial solution, just like you did...:
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% for post in posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
What you want to do/did is filter the 'posts' variable with the page language. This can indeed be done with assign using a where filter, but should look like this (as 'lang' is a attribute of the individual post and not of the category):
{% assign lang_posts = posts | where:"lang", page.lang %}
This leads to the following code:
{% for category in site.categories %}
<li><a name="{{ category | first }}">{{ category | first }}</a>
<ul>
{% for posts in category %}
{% assign lang_posts = posts | where:"lang", page.lang %}
{% for post in lang_posts %}
<li>{{ post.title }}</li>
{% endfor %}
{% endfor %}
</ul>
</li>
{% endfor %}
WITHOUT the built-in category solution of Jekyll
If you have a random list of categories in your front matter, like this:
- categories:
- web
- css
- internet
... and your sites _config.yml contains a similar (but more complete) list, like this:
- categories:
- web
- css
- internet
- html5
- jekyll
You have a whole other problem. In that case you are NOT using the built-in category solution of Jekyll and this solution does not apply. A lot of the statements mean different things in that case, like this:
{% for category in site.categories %}
{{ category | first }}
It means you loop over all existing categories from the _config.yml, and category | first should then be category. In this situation you probably want something like this:
<ul class="categories">
{% for category in site.categories %}
<li><a name="{{ category }}">{{ category }}</a>
<ul>
{% assign posts=site.posts | where:"lang", page.lang %}
{% for post in posts %}
{% if post.categories contains category %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Note that this is just simple Jekyll/liquid array logic that overwrites the Jekyll category variables.

Jekyll Grouping and Sorting Collection by Category and Priority

I have a bunch of documentation pages in a collection, which all have a Category, which I am using a group_by filter on like so:
{% assign docs_by_category = site.documentation | group_by: "category" %}
Then I use these groupings to create a menu structure, where items for each category are listed under a header. This is all good.
However, my problem lies in, being able to sort, which category is shown first, so I would like to be able to somehow prioritize them.
Say, if I have a the following categories: tutorials, getting started, advanced. I wouldn't want advanced to show up as the first category, but rather getting started.
The current code for generating the menu looks something like:
{% assign navurl = page.url | remove: 'index.html' %}
{% assign docs_by_category = site.documentation | group_by: "category" | reverse %}
{% for category in docs_by_category %}
<div class="category_wrapper">
<div class="category">{{ category.name }}</div>
<ul>
{% for item in category.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
TL;DR: How do I sort groups by some kind of priority?
You can use a configuration array to store categories order :
# _config.yml
categories-order:
- getting started
- tutorials
- advanced
Then :
{% assign docs_by_category = site.documentation | group_by: "category" %}
{% for cat in site.categories-order %}
{% assign currentCat = docs_by_category | where: 'name', cat | first %}
<div class="category_wrapper">
<div class="category">{{ currentCat.name }}</div>
<ul>
{% for item in currentCat.items %}
<li class="collapsed">
<a href="{{ site.baseurl }}{{ item.url }}">
{% if item.url == navurl %}
<u>{{ item.title }}</u>
{% else %}
{{ item.title }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
This implies that you reference all you categories in you config, otherwise, they will not appear in this listing.

Filter subpages and add classes in Jekyll

I'm using node sorting to determine pages within a subfolder in Jekyll. This image shows my folder structure:
So let's say I have a page at http://localhost/grain/ahvsuperb/ (shown below as index.html). Navigating to this page would show the class on added to that link. That works fine.
Where I'm running into trouble is when I navigate to a page such as http://localhost/grain/ahvsuperb/features/. When I navigate to that page, both index AND features have the on class added to them. Any insight as to how to filter that out with Liquid?
<div class="content_nav">
<ul class="content_nav">
{% assign sorted_pages = site.pages | sort: 'weight' %}
{% for node in sorted_pages %}
{% if node.title != nil and node.url != '/' %}
{% capture nodediff %}{{ page.url | remove:node.url }}{% endcapture %}
<li><a {% if nodediff != page.url %}class="on" {% endif %}href="{{node.url}}">{{node.title}}</a></li>
{% endif %}
{% endfor %}
<li style="float:right"><a class="brochured" href="javascript:void(0);" rel="#brochuredl">Brochure</a></li>
<li style="float:right"><a class="quoted" href="#">Get a Quote</a></li>
</ul>
</div>
Thanks!
Your {% capture nodediff %}{{ page.url | remove:node.url }}{% endcapture %} then {% if nodediff != page.url %} are not returning expected page determination.
This will do the job :
<div class="content-nav">
<ul class="content-nav">
{% assign sorted_pages = site.pages | sort: 'weight' %}
{% for node in sorted_pages %}
{% if node.title != nil and node.url != '/' %}
<li><a {% if node.url == page.url %}class="on" {% endif %}href="{{node.url}}">{{node.title}}</a></li>
{% endif %}
{% endfor %}
<li style="float:right"><a class="brochured" href="javascript:void(0);" rel="#brochuredl">Brochure</a></li>
<li style="float:right"><a class="quoted" href="#">Get a Quote</a></li>
</ul>
</div>

How to link post categories in Jekyll

I've got the following code in my index.html for Jekyll. I'm trying to find a way to link the categories associated with each post to the actual post themselves. So, if a post contains the category "travel" I want to click on a link that says "travel" which will bring me to all posts categorized as such.
<ul class="post-list" style="list-style-type: none;">
{% for post in paginator.posts %}
{% unless post.categories contains 'portfolio' %}
<li>
<h3>{{ post.title }}</h3>
<span class="post-meta">{{ post.date | date: "%c" }}</span>
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }} //problem area
{% endfor %}
{% endunless %}
{{ post.excerpt }} Find out more...<br><br>
</li>
{% endunless %}
{% endfor %}
</ul>
Figured it out. For anyone else wondering how to do the same, first setup a categories.html page in your root directory. This page will list all posts that meet a specific category. It does by turning the category names into named anchor slugs as such <a href="#{{ category | first | remove:' ' }}" and then the preceding code creates the actual named anchor div which displays the post associated with that category. Finally, under the page or section where you want to display the list of categories, you present the final bit of code which links to the named anchor section in your categories.html page.
First piece of code to go into Categories.html:
<h2>Posts by Category</h2>
<ul>
{% for category in site.categories %}
<li><strong>{{ category | first }}</strong></li>
{% if forloop.last %}
{% else %}
{% endif %}
{% endfor %}
</ul>
Second piece of code to go into Categories.html:
{% for category in site.categories %}
<div class="catbloc" id="{{ category | first | remove:' ' }}">
<h2>{{ category | first }}</h2>
<ul>
{% for posts in category %}
{% for post in posts %}
{% if post.url %}
<li>
<a href="{{ post.url }}">
<time>{{ post.date | date: "%B %d, %Y" }}</time> -
{{ post.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
</div>
{% endfor %}
Third piece of code to go where you want to display your named anchor linked categories:
Filed In:
{% unless p.categories == empty %}
{% for categories in post.categories %}
{{ categories }}
{% endfor %}
{% endunless %}
Use the following CSS to prevent the sections from displaying prematurely before you click on them:
.catbloc:not(:target){
display: none;
}
Hope this helps!