Jekyll tags that have spaces or multiple words - ruby

I'm using tags in Jekyll for my blog posts. The following is an example of tags I declare in the front matter in the Markdown file:
---
tags: [jekyll, tags, user experience]
---
The problem I have is that the "user experience" is rendered with a space, which breaks the link for the tag. I'd like to know if it's possible to have tags that have spaces or multiple words.
This is what my code looks like:
Markup with Ruby:
{% if page.tags.size > 0 %}
<div class="post-tags">
<ul>
{% for tag in page.tags %}
<li>{{ tag }}{% if forloop.last == false %},{% endif %}</li>
{% endfor %}
</ul>
</div>
{% endif %}
Does anyone have any ideas on how I can do that? Thanks!

You can use url_encode filter : {{ tag | url_encode }}.
Note that url_encode will turn a space into a + sign instead of a percent-encoded character. cf. Liquid doc

I had the same problem when I created the tag pages in my blog.
My solution was simply to replace the blanks by dashes:
---
tags: [jekyll, tags, user-experience]
---
Example front-matter from one of my posts:
tags:
- backup
- bitbucket-backup
- roboshell-backup
- source-control
The finished HTML looks like this:
<p><small>tags:
<span class="blog_post_categories">
backup,
bitbucket-backup,
roboshell-backup,
source-control
</span>
</small></p>
I'm sure that there is a more elegant solution which displays the blanks actually as blanks, but for me, dashes were good enough.

I discovered a fix. When you have spaces in the tags, e.g., "user experience", Jekyll isn't concatenating the words as part of the rendered link, leaving the space between both words: your.site/tags/user experience
I needed the two words joined by a + because my Tags page rendered a link to that section as #user+experience. So I added replace like this:
{{ tag | replace: " ","+" }}
This still feels like a bug in Jekyll, but this process works. Replace the space with whatever syntax you need.

Related

How to apply correct filter to {{ current_tags }} to display tag names not tag slugs

I want to alter this code to display the text within the anchor in readable format. I wish to display the tags name or title but it is currently displaying the tags as slugs.
Here is the snippet:
<!-- Creates the link as a variable, which is in 'tag_slug+tag_slug' format -->
{%- capture tag_url -%}{{ collection.url }}/{{ current_tags | join: "+"}}{%- endcapture -%}
<!-- HELP ME display the text inside the anchor in 'tag_title + tag_title' format. It currently displays the text of the anchor in 'tag_slug + tag_slug' format which is not very readable -->
<a class="breadcrumbs__link" href="{{ tag_url }}" aria-current="page">{{ current_tags | join: " + "}}</a>
I have tried a handful of things but have been unsuccessful. One of which was:
{{ current_tags.title | join: " + " }}
What is the equivalent to current_tags.title in liquid?
Here is an example output of the link:
<a class="breadcrumbs__link" href="/collections/example/tag-one+tag-two" aria-current="page">tag-one + tag-two</a>
This would be much easier to follow as a user if it was:
<a class="breadcrumbs__link" href="/collections/example/tag-one+tag-two" aria-current="page">Tag one + Tag two</a>
Where the tags would not have spaces removed, and the first letter will be capitalised. One thing to consider is that some tags require the dash to remain if it is already present in the tag title. So replacing dashes for spaces will not work. It will just create another issue.
EDIT - Another method that does not work:
{%- capture tag_url -%}{{ collection.url }}/{{ current_tags | join: "+"}}{%- endcapture -%}
<a class="breadcrumbs__link" href="{{ tag_url }}" aria-current="page">
{% for tag in collection.all_tags %}
{% if current_tags contains tag %}
<div class="current-tag">{{ tag.title }}</div>
{% endif %}
{% endfor %}
</a>
EDIT 2 - Another failed attempt:
{% for tag in current_tags %}
<div class="current-tag">{{ tag.title }}</div>
{% endfor %}
This solution is not by the book. I have not been able to find any documentation on how to iterate through the current_tags, display the tags by their readable name & add a join value such as ' + '.
However I have managed to loop through the current tags, then use CSS to change their appearance to better suit the style we wished for. So again this is not the textbook way of acheiving this and the results will not be 100% correct all the time but it is a substantial improvement on the previous display.
This will take the value of for example 'balances-scales-accessories+bench-scales' and convert it into 'Balances Scales Accessories + Bench Scales'. The 100% answer would look a little more like 'Balances, Scales & Accessories + Bench Scales'.
Where this solution fails is:
The ',' and '&' characters are removed.
The '-' are removed when in very niche situations these are actually a desired character like in the word 'Semi-gloss'.
Liquid:
<!-- Generate link as previously achieved -->
{%- capture tag_url -%}{{ collection.url }}/{{ current_tags | join: "+"}}{%- endcapture -%}
<!-- Add generated link to anchor tag -->
<a class="breadcrumbs__link" href="{{ tag_url }}" aria-current="page">
<!-- Loop through tags present in current tags list -->
{% for tag in current_tags %}
<!-- Wrap echoed tag in a span so it is targetable, replace the dashes for spaces, and make the whole thing lowercase -->
<span class="breadcrumb-tag">{{ tag | replace: '-', ' ' | downcase }}</span>
{% endfor %}
</a>
CSS:
/* Capitalise first letter of each word of the breadcrumb */
.breadcrumbs__link .breadcrumb-tag {
text-transform: capitalize;
}
/* Add a before on each tag that is not the first, to display a '+' symbol indicating there are multiple tags selected */
.breadcrumbs__link .breadcrumb-tag + .breadcrumb-tag:before {
content: ' + ';
}
Again not perfect but will do until someone can create a more reliable solution in liquid.

Overriding a context register in a Jekyll Block plugin

I'm trying to get more control over my post excerpts in Jekyll. Using the default excerpt code doesn't seem to work well in my case, as it (a) needs to be the first paragraph, and (b) is still rendered on the page. For most of my blog posts, I want either custom text, which isn't meant to be part of the actual blog post, or some text later on in the blog post. I was hoping to do this with a block, that can optionally take a parameter to render the content (although I'll add in that specific feature later). I've currently got this proof-of-concept plugin:
require "jekyll"
module Jekyll
class RenderExcerptBlock < Liquid::Block
def render(context)
page = context.registers[:page]
content = super
page["excerpt"] = content.lines[0..50]
content
end
end
end
Liquid::Template.register_tag "excerpt", Jekyll::RenderExcerptBlock
Which would theoretically let me do this:
{% excerpt %}
A snippet of this post
{% endexcerpt %}
However, this gives the error when running jekyll build:
Liquid Exception: Key excerpt cannot be set in the drop. in /Users/nick.chambers/blog/_posts/2020-01-21-unix-shell-scripting-with-bash.md
ERROR: YOUR SITE COULD NOT BE BUILT:
------------------------------------
Key excerpt cannot be set in the drop.
Is it possible to override this register's value in some way? Alternatively, is there a more Jekyll way to achieve this?
The Jekyll way to have a custom excerpt for your post (or any document in a collection), is to simply add it to the front matter:
---
title: Test Doc
excerpt: This is a custom excerpt
---
The opening paragraph of this document.
Another paragraph in this document.
Then reference it in your layout to render it:
<div class="post-excerpt">
{{ page.excerpt }}
</div>
or
<div>
{% for post in site.posts %}
<h2>{{ post.title }}</h2>
<div>{{ post.excerpt }}</div>
{% endfor %}
</div>

Frontmatter automation and category sorting in Jekyll

I am new to Jekyll, and I am working on a site where I want to have a navigation menu that uses the category names as the link text. However, I don't want the cat names ordered alphabetically or reversed, but in a different order. The only thing I could come up with is, defining a hash in the config file like this:
cats:
"a": "dogs"
"b": "cats"
"c": "spiders"
"d": "jiraffes"
and then for the navigation I have something like this (please don't laugh at this noobie, he he):
<ul>{% for cat_hash in site.cats %}{% for cat in cat_hash %}{% for page in site.pages %}{% if cat[1] == page.category %}
<li>{{ page.category }}</li>{% endif %}{% endfor %}{% endfor %}{% endfor %}
</ul>
Now, since I have many pages under each category, I would like to automate the process a bit, so I'm trying to use liquid code in the front matter like this:
---
layout: default
category: {{ site.cats["a"] }}
---
but of course this doesn't work. I've searched SO and found a solution using a plugin, but I can not use plugins for this site. Anybody has any idea? What I would like to do is:
Have the categories sorted in any order I want, (not alphabetically).
Automate the cat name generation in the front matter
Thank you in advance.
A possible solution :
Ordering is not the problem and I think that the _config.yml seems to do it.
The problem is to automatically match a page to a category without having to write the category name in the pages's front matter.
My idea is then to match a category to a folder. Any file present in the cats folder will be considered to be part of the cats category, and then appear in the right menu.
--cats
|--cat1.md
|--cat2.md
|--
--dogs
|--dog1.md
|--dog2.md
|--
--spiders
|--spider1.md
Then the _config.yml can be changed a little to give a match between folder name and display in the menu.
categories:
dogs:
display: Doggies
cats:
display: I love catz
spiders:
display: Spiders
Now we can easily match our pages to a category and display everything in a menu :
{% for cat in site.categories %}
<h2>{{ cat[1].display }}</h2>
<ul>
{% for page in site.pages %}
{% if page.dir contains cat[0] %}
<li>{{ page.title }}</li></li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
Et voilà !

Multiple Blogs In Single Jekyll Website

Is there a way I can have a single Jekyll website have more than one blog? I currently want to have two blogs in one site.
I am the author of the page http://www.garron.me/blog/multi-blog-site-jekyll.html
Considering that you need individual archives pages, and latest post per individual blog. Just use something like this:
Create a file archives-blog-1.html and fill it with:
{% for post in site.posts %}
{% if post.categories contains 'blog1' %}
<div class="post">
<h3 class="title">{{ post.title }}</h3>
<p class="meta">Date: {{ post.date }}</p>
</div>
{% endif %}
{% endfor %}
That will give you a list of all post in blog1, you can do the same for blog2. That page can be anyplace you want.
For the latest post, you can use the same code but enclosed between:
{% for post in site.posts limit:5 %}
....
{% endfor %}
That will give you the lastes 5 posts... I am using this
{% for post in site.posts limit:5 %}
<div class="post">
<ul>
<li>{{ post.title | truncate:200 }} <small>{{ post.date }}</small>
{% if post.summary %}
<p class="entry">{{ post.summary }}</p>
{% endif %}
</li>
</ul>
</div>
{% endfor %}
In my index page. http://www.garron.me/index.html ... under the sub-title (From the blogs)
I am not limiting to any category, so posts from all blogs appear there, you can limit with {% if post.categories contains 'blog1' %}
Hope it helps you.
There's a simpler solution than any of the answers so far.
Folder structure:
- blog1/
- _posts/
- blog2/
- _posts/
Then in the index.html for blog1, use site.categories.blog1 instead of site.posts.
See the documentation for "site.categories" and "page.categories" in https://jekyllrb.com/docs/variables/
I used two separate Jekyll installations to run two blogs on the same domain; if your blogs are going to live in separate root dirs (mine are at / and /photos/), then I'd recommend this approach. I also described how I merged both blogs' sitemap.xml files.
Your best bet would be to look into the data files feature. You can put .markdown files in a separate folder in your source and link to them as you post. This does mean that in order to make a post, you'll need to write a data file entry, but you can host as many "blogs" as you'd like, each with their own folder. Posts will automatically have the folder they're in as the url. I use this method for my own personal blog and portfolio.
Either that, or you may want to look into collections: http://jekyllrb.com/docs/collections/
Adding to #ggarron's answer, there's a short version of filtering by categories:
{% assign filtered_posts = site.posts | where_exp: "post", "post.categories contains 'blog1'" %}
The advantage of assigning to a variable is that one can replace all uses of site.posts with filtered_posts and keep consistency. For example, I have a snippet to get the first post of the list:
{% assign first_post = filtered_posts | first %}
This works as expected.

An easy way to support tags in a jekyll blog

I am using the standard jekyll installation to maintain a blog, everything is going fine. Except I would really like to tag my posts.
I can tag a post using the YAML front matter, but how do I generate pages for each tag that can will list all posts for a tag?
Here is a solution with alphabetically sorted tags on a single page.
It uses Liquid only, which means that it works on GitHub Pages:
{% capture tags %}
{% for tag in site.tags %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<h3 id="{{ tag }}">{{ tag }}</h3>
<ul>
{% for post in site.tags[tag] %}
<li>{{ post.title }}</li>
{% endfor %}
</ul>
{% endfor %}
You can see it in action here.
EDIT:
There's also a way to generate a separate page for each tag without plugins (which will work on GitHub Pages).
I have a more detailed explanation on my blog:
Separate pages per tag/category with Jekyll (without plugins)
First, you need a new layout file:
/_layouts/tagpage.html:
---
layout: default
---
<h1>{{ page.tag }}</h1>
<ul>
{% for post in site.tags[page.tag] %}
<li>
{{ post.date | date: "%B %d, %Y" }}: {{ post.title }}
</li>
{% endfor %}
</ul>
With this layout file, you can add a new tag page by adding a new file with just two lines of YAML front-matter.
Here's an example for the jekyll tag:
/tags/jekyll/index.html:
---
layout: tagpage
tag: jekyll
---
The only disadvantage of this approach: each time you use a new tag for the first time, you have to remember to create a new two-line file for it.
To generate the root index file (i.e. the list of tags that links to /tags/jekyll/index.html etc.), you can use a similar solution like the one on top of this answer where I generate a single page with alphebetically sorted tags:
{% capture tags %}
{% for tag in site.tags %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
{{ tag }}<br>
{% endfor %}
This will generate a list of links like this:
<ul>
<li>.net</li>
<li>authentication</li>
<li>backup</li>
</ul>
Note that this solution uses a blank to split tags, so it doesn't work when your tags contain blanks and Yevgeniy Brikman's comment applies here as well.
This gist will generate a page per category for you: https://gist.github.com/524748
It uses a Jekyll Generator plugin, plus a Page subclass.
Have a look at sites using jekyll. There are a few custom forks which have implemented tagging functionality, hopefully also in the way you want :-)
I had the same question, and stumbled upon this: http://gist.github.com/143571.
It's a rake task which generates a tag list. I modified it slightly, and my version is at:
http://github.com/mattfoster/mattfoster.github.com/blob/master/Rakefile.
Whilst this doesn't give you a page per tag, you can use anchors, which is half way there!
I use the great Jekyll Tagging plugin that automatically generates a tags cloud and tag pages. Easy to install and use.
Here is a page for the "photo" tag on my blog (in french), and you can see the tags cloud in the bottom.
Based on Christian's answer above I made a bash script that does what he described.
https://github.com/ObjectiveTruth/objectivetruth.github.io/blob/master/rebuild_tags.sh
Be sure to have the accompanying 14 line vim script in the /non_website_resources/ directory
AND
Make the /_layouts/tagpage.html shown in Christian's answer above but rename it to /_layouts/tag_pages.html
File structure should be like this:
.jekyll_website_root
├── _posts
├── _layout
│ ├── tag_pages.html
├── rebuild_tags.sh
Run from the root directory ./rebuild_tags.sh
If you get permission denied error be sure to run chmod 777 rebuild_tags.sh
If you look at scripts comments its fairly simple:
Uses sed to find all the tags in every .md file in _post directory
Uses sed to massage the data to proper format
Takes all the unique tags and makes a directory and a index.html for each
This way, if you have any new tags, just run the script to rebuild the pages before pushing to github
A nice simple non-plugin way to do tags
EDIT
Removed dependency on other files. Just need the one script!
I do these with CSS. First lists an element and use the tag name as its id.
<span id="{{ site.posts | map: 'tags' | uniq | join: '"></span><span id="' }}"></span>
And then lists all the post and use its tags as a value for the "tags" custom attribute.
{% for post in site.posts %}
<article class="post" tags="{% for tag in post.tags %}{{tag}}{% if forloop.last == false %}{{" "}}{% endif %}{% endfor %}">
<h3>{{post.title}}</h3>
</article>
{% endfor %}
And then in CSS, hide all the posts by default, and only show posts with tags matches the url id/ hash
.post {
display: none;
}
{% for tag in site.tags %}#{{tag[0]}}:target ~ [tags~={{tag[0]}}]{% if forloop.last == false %}, {% endif %}{% endfor %} {
display: block;
}
/*
The compiled version will look like this
#tagname:target ~ [tags~="tagname"], #tagname2:target ~ [tags~="tagname2"] {
display: block;
}
*/
I made an article about this here.

Resources