在Jekyll网站上实现面包屑导航的一些好方法是什么?

13

我知道http://raphinou.github.com/jekyll-base/中有单层面包屑,但我正在寻找一些好的方法,当目录深度达到四或五级时,在Jekyll网站上放置面包屑导航

(是的,我很清楚 Jekyll 主要是用于博客引擎,也许我不应该在它上面使用许多目录级别的通用网站。我也知道http://octopress.org,但没有找到合适的插件。)

基于http://forums.shopify.com/categories/2/posts/22172的思路,我开发了以下 Jekyll 布局用于生成面包屑导航。您可以在http://crimsonfu.github.com/members/pdurbin 上看到其效果,页面顶部显示了"home » members »" 的面包屑导航。

这是我的布局模板,不太美观。我还没有深入学习 Liquid。您能否建议更好的方法?

<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> &nbsp; </li>
{% elsif mySecondWord contains '.html' %}
  <li><a href="/">home</a> &#187; </li>
  {% unless mySecondWord == 'index.html' %}
  <li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> &#187; </li>
  {% endunless %}
{% elsif myThirdWord contains '.html' %}
  <li><a href="/">home</a> &#187; </li>
  <li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> &#187; </li>
  {% unless myThirdWord == 'index.html' %}
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> &#187; </li>
  {% endunless %}
{% elsif myFourthWord contains '.html' %}
  <li><a href="/">home</a> &#187; </li>
  <li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> &#187; </li>
  {% unless myFourthWord == 'index.html' %}
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> &#187; </li>
  {% endunless %}
{% elsif myFifthWord contains '.html' %}
  <li><a href="/">home</a> &#187; </li>
  <li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> &#187; </li>
  {% unless myFifthWord == 'index.html' %}
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}/{{myFourthWord}}">{{myFourthWord}}</a> &#187; </li>
  {% endunless %}
{% else %}
  <li><a href="/">home</a> &#187; </li>
  <li><a href="/{{myFirstWord}}">{{myFirstWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}">{{mySecondWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}">{{myThirdWord}}</a> &#187; </li>
  <li><a href="/{{myFirstWord}}/{{mySecondWord}}/{{myThirdWord}}/{{myFourthWord}}">{{myFourthWord}}</a> &#187; </li>
{% endif %}
</ul>
</div>
<h1>{{ page.title }}</h1>
{{ content }}
</body>
</html>

1
Jekyll面包屑导航插件的另一篇文章:http://biosphere.cc/software-engineering/jekyll-breadcrumbs-navigation-plugin - Erlend
一个很棒的示例和代码:https://jekyllcodex.org/without-plugin/breadcrumbs/ - Gabriel Staples
12个回答

22

我稍微改进了之前给出的答案。我删除了无序列表,并用字符(正斜杠)分隔项。我添加了一个过滤器,用于支持像“mysite.com/path/index.html”和“mysite.com/path/item-name.html”这样的URL。最后,我将标题大写。结果看起来像这样:

首页 / 路径 / 项目名称

{% 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 %}

顺便说一句,我创建了一个在线资源,供像这样的片段使用:jekyllcodex.org/without-plugins


1
谢谢!这很简洁、简单,而且有效。我很遗憾只能投一票(它在SO上真的值得更多的投票)。 - SexxLuthor
这太棒了。我们能否在a标签中使用页面标题而不是URL? - Sharath kumar
你可以这样做,但需要循环遍历所有页面并匹配URL。这会使您的构建变慢。我不会这样做。 - Mr. Hugo
1
我已经创建了自己的代码片段资源,类似于这个网站:http://jekyllcodex.org/without-plugin/ - Mr. Hugo
@JoostS,您提到可以循环遍历锚点并获取页面标题而不是使用URL。即使速度较慢,您建议如何进行?我想可以自动构建一个JSON文件来存储这些数据,然后面包屑从中提取,但我想知道是否在breadcrumbs.html中还有其他方法,我可能错过了。 - ekfuhrmann
@ekfuhrmann 有些晚了...但根据您的页面结构,您可以做类似这样的事情{% for c in site.pages | where: 'slug',crumb %},但我不会这么做。使用JSON解决方案更明智。当此方法无法正常工作(当我的页面标题较为复杂)时,我使用液态替换,例如:{{ crumb | replace:'your title','Your Title' }}。 - Mr. Hugo

7
这应该在任何深度下提供面包屑导航(有一个警告,请参见结尾)。不幸的是,Liquid 过滤器相当受限,因此这是一种不稳定的解决方案:每当出现 /index.html 时,它都会被删除,这将破坏具有以 index.html 开头的文件夹的 URL(例如 /a/index.html/b/c.html),希望这种情况不会发生。
{% 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> &nbsp; </li>
 {% else %}
  <li><a href="/">home</a> &#187; </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> &#187; </li>

   {% capture url_parts %}{{ url_parts | remove_first:first_word }}{% endcapture %}
  {% endfor %}
 {% endif %}
</ol>

它的工作原理如下:

  • 分离URL,忽略index.html(例如/a/b/index.html变成a b/a/b/c.html变成a b c.html),
  • 连续地获取并删除url_parts的第一个单词,以迭代除最后一个单词外的所有单词(例如,它会进入a b c.html -> (a, b c.html) -> (b, c.html);然后停止)。
  • 在每个步骤中,使用当前的first_word和之前已经查看过的所有上级目录(对于上述示例,它将依次生成面包屑链接"" -> "/a" -> "/a/b")。
注意,for循环中的page.content只是为了提供迭代内容,魔法是由limit:num_parts完成的。但是,这意味着如果page.content的段落比num_parts少,不会出现所有的面包屑导航,如果可能的话,可以在_config.yml中定义一个站点变量,例如breadcrumb_list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],并使用site.breadcrumb_list作为占位符,而不是page.content。
这里有一个例子(链接到这里),虽然它没有完全使用上述代码,但只是进行了一些小修改。

我在处理 page.content 时遇到了问题,所以按照您的建议在 _config.yml 中定义了一个数组。谢谢! - Philip Durbin
有一点小问题,这可能应该是一个<ol>,因为面包屑导航有特定的顺序:) http://developers.whatwg.org/grouping-content.html#the-ol-element - imjared
@imjared,花了一点时间,但现在已经修复了。 :) - huon
哎呀,stackoverflow的声誉现在已经恢复了 ;) - imjared
将文件夹命名为 index.html 似乎本来就是个糟糕的想法,但还是谢谢你提醒。 - ThisClark
这个是否应该输出当前页面? - tyteen4a03

3
我用liquid模板制作了下面的面包屑导航,尽可能地优雅:)它使用jQuery在最后一个li项上设置active类。
            <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">&#187;</span> {{ crumb | remove: '.html' }} </li>
                    {% endunless %}
                {% endfor %}
            </ul>
                            <script>$(".breadcrumb li").last().addClass('active');</script>

2
我找到了一种不完全自动但适用于GitHub页面的替代技术。
它包括创建一个数据文件,其中列出可能的路径列表。 例如,这是我网站上的_data/breadcrumbs.csv文件:
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

然后,一个简单的循环创建面包屑导航:
<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>

详细信息和实现链接请参见此文章


2
我考虑使用自动面包屑导航,并从frontmatter中显示漂亮的面包屑元素文本。

这在GitHub Pages上完美地工作

在这里查看: http://blog.comsysto.com/2015/04/25/automatic-breadcrumb-for-jekyll-on-github-pages/

如果您有以下设置:

/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>

面包屑将会呈现以下内容。
<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>

2

以下是我的解决方案,适用于截至今天的Jekyll 3.1.3和GitHub Pages。与其他人提供的一些解决方案类似,它仅检查页面的URL并从中构建面包屑。

{% 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 %}

2
这个插件似乎更加健壮。

有人知道这个插件是否适用于Jekyll 2.3.0吗? - JCraine
1
@JCraine:没错。几天前更新了代码,现在也可以处理Jekyll文章。 - snøreven
太好了!感谢您抽出时间回复。 :) - JCraine
不幸的是,我无法让它工作。将代码添加到 plugin.rb 中会导致构建立即失败。还有其他人有运气吗? - JCraine

2
这是我在继承的网站中实现面包屑导航的方法,它基于其他版本。在我们的情况下,一些文件夹中没有index.html页面。因此,单击链接到没有index.html的文件夹的面包屑将导致错误。这可以通过更好的文件结构来消除,但我无法改变它。
这是我想出的解决方案。
<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>

1

我想将此添加到混合中。这基于Davelab6上面的示例,有一些改进。活动类由循环中的最后一个条目设置-还包含每个面包屑的永久链接。

我还没有测试过它是否适用于帖子-但应该可以工作。如果有任何问题,请告诉我。

<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>

谢谢。这个对我很有效。为了改进它,我添加了 {{ site.url }} 以确保即使在 GitHub-Pages 上也能正常工作。在我的主题 http://phlow.github.io/feeling-responsive/design/breadcrumb/ 中使用它。 - Phlow
很好!我喜欢你的主题 - 为开源贡献点赞。我已经更新了下面的代码,如果你想看一下,我把它变得更简单了。 - JCraine
很好。更加简洁易读易懂了。谢谢你让我知道。我已经更新了我的代码。 - Phlow

1

我成功地将其简化了。这是我现在正在使用的:

{% 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接