问题
I'm aware that there are single-level breadcrumbs in http://raphinou.github.com/jekyll-base/ but I'm looking for some good ways to have breadcrumbs on a Jekyll site when directories get to a depth of four or five levels.
(Yes, I'm well aware that Jekyll is primarily a blogging engine and that perhaps I shouldn't use it for a general purpose website, especially with many directory levels. I'm also aware of http://octopress.org but haven't found a suitable plugin.)
Based heavily on http://forums.shopify.com/categories/2/posts/22172 I came up with the following Jekyll layout for breadcrumbs, a variation of which you can see in action at http://crimsonfu.github.com/members/pdurbin . You should see the breadcrumbs "home » members »" at the top.
Here's my layout. Yes, it's ugly. I haven't studied Liquid much. Can you suggest a better way?
<html>
<head>
<title>{{ page.title }}</title>
<style type="text/css">
#bread ul {
padding-left: 0;
margin-top: 2px;
margin-bottom: 2px;
}
#bread ul li {
display: inline;
font-size: 70%;
}
</style>
</head>
<body>
<div id="bread">
<ul>
{% assign url = {{page.url}} %}
{% assign delimiter = '/' %}
{% capture allparts %}{{ url | replace: delimiter, ' ' }}{% endcapture %}
{% capture myFirstWord %}{{ allparts | truncatewords: 1 | remove: '...' }}{% endcapture %}
{% capture minusFirst %}{{ allparts | replace_first: myFirstWord, '' }}{% endcapture %}
{% capture mySecondWord %}{{ minusFirst | truncatewords: 1 | remove: '...' }}{% endcapture %}
{% capture minusSecond %}{{ minusFirst | replace_first: mySecondWord, '' }}{% endcapture %}
{% capture myThirdWord %}{{ minusSecond | truncatewords: 1 | remove: '...' }}{% endcapture %}
{% capture minusThird %}{{ minusSecond | replace_first: myThirdWord, '' }}{% endcapture %}
{% capture myFourthWord %}{{ minusThird | truncatewords: 1 | remove: '...' }}{% endcapture %}
{% capture minusFourth %}{{ minusThird | replace_first: myFourthWord, '' }}{% endcapture %}
{% capture myFifthWord %}{{ minusFourth | truncatewords: 1 | remove: '...' }}{% endcapture %}
{% if myFirstWord contains '.html' %}
<li><a href="/">home</a> </li>
{% elsif mySecondWord contains '.html' %}
<li><a href="/">home</a> » </li>
{% unless mySecondWord == 'index.html' %}
<li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> » </li>
{% endunless %}
{% elsif myThirdWord contains '.html' %}
<li><a href="/">home</a> » </li>
<li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> » </li>
{% unless myThirdWord == 'index.html' %}
<li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> » </li>
{% endunless %}
{% elsif myFourthWord contains '.html' %}
<li><a href="/">home</a> » </li>
<li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> » </li>
{% unless myFourthWord == 'index.html' %}
<li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> » </li>
{% endunless %}
{% elsif myFifthWord contains '.html' %}
<li><a href="/">home</a> » </li>
<li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> » </li>
{% unless myFifthWord == 'index.html' %}
<li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}/{{myFourthWord}}">{{myFourthWord}}</a> » </li>
{% endunless %}
{% else %}
<li><a href="/">home</a> » </li>
<li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> » </li>
<li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}/{{myFourthWord}}">{{myFourthWord}}</a> » </li>
{% endif %}
</ul>
</div>
<h1>{{ page.title }}</h1>
{{ content }}
</body>
</html>
回答1:
This should give breadcrumbs at any depth (with a caveat, see end). Unfortunately, the Liquid filters are fairly limited, so this is an unstable solution: any time /index.html
appears, it is deleted, which will break URLs that have a folder that starts with index.html
(e.g. /a/index.html/b/c.html
), hopefully that won't happen.
{% capture url_parts %} {{ page.url | remove: "/index.html" | replace:'/'," " }}{% endcapture %}
{% capture num_parts %}{{ url_parts | number_of_words | minus: 1 }}{% endcapture %}
{% assign previous="" %}
<ol>
{% if num_parts == "0" or num_parts == "-1" %}
<li><a href="/">home</a> </li>
{% else %}
<li><a href="/">home</a> » </li>
{% for unused in page.content limit:num_parts %}
{% capture first_word %}{{ url_parts | truncatewords:1 | remove:"..."}}{% endcapture %}
{% capture previous %}{{ previous }}/{{ first_word }}{% endcapture %}
<li><a href="{{previous}}">{{ first_word }}</a> » </li>
{% capture url_parts %}{{ url_parts | remove_first:first_word }}{% endcapture %}
{% endfor %}
{% endif %}
</ol>
How it works is:
- separates the URL, ignoring
index.html
(e.g./a/b/index.html
becomesa b
,/a/b/c.html
becomesa b c.html
), - successively takes and removes the first word of
url_parts
, to iterate through all but the last word (e.g. it goesa b c.html
-> (a
,b c.html
) -> (b
,c.html
); then we stop). - at each step, it makes the breadcrumb link using the current
first_word
, andprevious
which is all the previous directories seen (for the example above, it would go""
->"/a"
->"/a/b"
)
NB. the page.content
in the for loop is just to give something to iterate over, the magic is done by the limit:num_parts
. However, this means that if page.content
has fewer paragraphs than num_parts
not all breadcrumbs will appear, if this is likely one might define a site variable in _config.yml
like breadcrumb_list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
and use site.breadcrumb_list
as the placeholder instead of page.content
.
Here is an example (it doesn't use precisely the same code as above, but it's just a few little modifications).
回答2:
I have improved slightly on the answers given earlier. I have removed the unordered list and seperated the items with a character (forward slash). I have added a filter for 'index.html' and '.html', so urls like 'mysite.com/path/index.html' and 'mysite.com/path/item-name.html' are also supported. Finally I have capitalized the titles. This results in something that looks like this:
Home / Path / Item name
{% assign crumbs = page.url | remove:'/index.html' | split: '/' %}
<a href="/">Home</a>
{% for crumb in crumbs offset: 1 %}
{% if forloop.last %}
/ {{ crumb | replace:'-',' ' | remove:'.html' | capitalize }}
{% else %}
/ <a href="{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}">{{ crumb | replace:'-',' ' | remove:'.html' | capitalize }}</a>
{% endif %}
{% endfor %}
PS. I have created an online resource for snippets like this: jekyllcodex.org/without-plugins
回答3:
I made the following breadcrumbs with liquid templates, its as elegant as I could manage :) It uses jQuery to set the active class on the li last li item.
<ul class="breadcrumb">
<li><a href="#"><i class="icon-home"></i>Home</a> </li>
{% assign crumbs = page.url | split: '/' %}
{% assign crumbstotal = crumbs | size %}
{% for crumb in crumbs offset:2 %}
{% unless crumb == 'index.html' %}
<li><span class="divider">»</span> {{ crumb | remove: '.html' }} </li>
{% endunless %}
{% endfor %}
</ul>
<script>$(".breadcrumb li").last().addClass('active');</script>
回答4:
This plugin seems a tad more robust.
回答5:
This is how I implemented breadcrumbs in a site I inherited, which is based off some of the other versions here. In our case, some of our folders didn't contain an index.html page. Therefore, clicking on a breadcrumb that linked to a folder with no index.html in it would cause an error. This could have been eliminated with a better file structure, but I wasn't able to change that.
Here is what I came up with.
<ol class="pull-right breadcrumb">
<li><a href="/">Home</a></li>
{% assign crumbs = page.url | split: '/' %}
{% assign crumbs_total = crumbs | size | minus: 1 %}
{% for crumb in crumbs offset: 1 %}
{% if forloop.index == crumbs_total %}
<li class="active"><a href="{{ site.baseurl }}{{ page.url }}">{{page.title}}</a></li>
{% else %}
{% assign crumb_limit = forloop.index | plus: 1 %}
{% capture crumb_url %}{% for c in crumbs limit: crumb_limit %}{{ c | append: '/' }}{% endfor %}{% endcapture %}
{% capture crumb_with_index %}{{ crumb_url | append: 'index.html' }}{% endcapture %}
{% capture current_page %}{{ site.baseurl }}{{ page.url }}{% endcapture %}
{% for p in site.pages %}
{% if crumb_with_index != current_page and crumb_with_index == p.url %}
<li><a href="{{ crumb_with_index }}">{{ crumb | replace:'-',' ' | capitalize}}</a>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</ol>
回答6:
Here's my solution, which works with Jekyll 3.1.3 and GitHub Pages as of today. Like some solutions others have given, it simply inspects the page's URL and builds the breadcrumbs from it.
{% unless page.hide-breadcrumbs %}
<ul class="breadcrumb">
<li><a href="/">{{site.title}}</a> </li>
{% assign crumbs = page.url | split: '/' %}
{% for crumb in crumbs offset:1 %}
<li {% if forloop.last %}class="active"{% endif %}>
{% unless forloop.last %}
<a href="/{% for crumb in crumbs offset:1 limit:forloop.index %}{{crumb}}/{% endfor %}">
{{ crumb | capitalize }}
</a>
{% else %}
{{ crumb | capitalize }}
{% endunless %}
</li>
{% endfor %}
</ul>
{% endunless %}
回答7:
I found an alternative technique that is not entirely automatic but that works on GitHub pages.
It consists of creating a data file with the list of possible paths.
For example, here is _data/breadcrumbs.csv
for my site:
url,title
/,Home
/api/,API
/api/jsonarray/,JsonArray
/api/jsonbuffer/,JsonBuffer
/api/jsonobject/,JsonObject
/api/jsonvariant/,JsonVariant
/doc/,Manual
/example/,Examples
/news/,News
/faq/,FAQ
Then, a simple loop creates the breadcrumb:
<ol class="breadcrumb">
{% for crumb in site.data.breadcrumbs %}
{% assign url_prefix = page.url | slice: 0, crumb.url.size %}
{% if (url_prefix == crumb.url) and (page.url != crumb.url) %}
<li class="breadcrumb-item">
<a href="{{ crumb.url | prepend: site.baseurl }}">
{{ crumb.title }}
</a>
</li>
{% endif %}
{% endfor %}
<li class="breadcrumb-item active">
<a href="{{ page.url | prepend: site.baseurl }}">
{{ page.title }}
</a>
</li>
</ol>
See this article for details and links to the implementation.
回答8:
Thought I might throw this into the mix. This is based on Davelab6's example above, with a few improvements. The active class is set by the last entry in the loop - also contains permalinks for each crumb.
I haven't tested it with posts yet - but it should work. Let me know if any issues.
<ul class="breadcrumbs">
<li><a href="/">Home</a></li>
{% assign crumbs = page.url | split: '/' %}
{% assign crumbs_total = crumbs | size | minus: 1 %}
{% for crumb in crumbs offset: 1 %}
{% if forloop.index == crumbs_total %}
<li class="active">{{ crumb | replace:'-',' ' }}</li>
{% else %}
<li><a href="{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}">{{ crumb | replace:'-',' ' }}</a>
{% endif %}
{% endfor %}
</ul>
回答9:
I managed to simply it even further. Here is what I'm using now:
{% assign crumbs = page.url | split: '/' %}
<ul class="lv-breadcrumbs">
<li><a href="/">Home</a></li>
{% for crumb in crumbs offset: 1 %}
{% if forloop.last %}
<li class="active">{{ crumb | replace:'-',' ' }}</li>
{% else %}
<li><a href="{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}">{{ crumb | replace:'-',' ' }}</a></li>
{% endif %}
{% endfor %}
</ul>
回答10:
I thought of an automatic breadcrumb also displaying nice breadcrumb element text from the frontmatter.
It works perfectly on GitHub Pages.
Check it out here: http://blog.comsysto.com/2015/04/25/automatic-breadcrumb-for-jekyll-on-github-pages/
If you have this setup:
/mypage1/index.html
---
layout: default
title: My Page 1 - My Homepage
breadcrumb: My Page 1
---
<div class="container">
<div class="row">
<div class="col-md-12">
peace yo!
</div>
</div>
</div>
/mypage1/subpage1/index.html
---
layout: default
title: My Sub Page 1 - My Homepage
breadcrumb: My Sub Page 1
---
<div class="container">
<div class="row">
<div class="col-md-12">
foobar!
</div>
</div>
</div>
The breadcrumb will render the following
<ol class="breadcrumb">
<li><a href="/">Home</a></li>
<li><a href="/mypage1/index.html">My Page 1</a></li>
<li class="active"><a href="/mypage1/subpage1/index.html">My Sub Page 1</a></li>
</ol>
回答11:
And I have improved JoostS's response to have the following capabilities:
- Replace the "/" path separator with a rightward-pointing triangle (▶ ▶)
If one exists, use the
breadcrumb:
from the front matter of the final page{% assign crumbs = page.url | remove:'/index.html' | split: '/' %} <a href="/">Home</a> {% for crumb in crumbs offset: 1 %} {% if forloop.last %} {% if page.breadcrumb != '' %} ▶ <a href="#">{{ page.breadcrumb }} </a> {% else %} ▶ <a href="#">{{ crumb | replace:'-',' ' | remove:'.html' | capitalize }}</a> {% endif %} {% else %} ▶ <a href="{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}">{{ crumb | replace:'-',' ' | remove:'.html' | capitalize }}</a> {% endif %} {% endfor %}
来源:https://stackoverflow.com/questions/9612235/what-are-some-good-ways-to-implement-breadcrumbs-on-a-jekyll-site