使用Jekyll和Liquid排序的导航菜单


68

我正在用Jekyll / Liquid构建一个静态网站(无博客)。我希望它具有一个自动生成的导航菜单,该菜单列出所有现有页面并突出显示当前页面。这些项目应以特定顺序添加到菜单中。因此,我weight在页面的YAML中定义了一个属性:

---
layout : default
title  : Some title
weight : 5
---

导航菜单的结构如下:

<ul>
  {% for p in site.pages | sort:weight %}
    <li>
      <a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
        {{ p.title }}
      </a>
    </li>
  {% endfor %}
</ul>

这将创建指向所有现有页面的链接,但是它们未排序,因此sort似乎忽略了该过滤器。显然,我做错了,但我不知道该怎么办。


我只是发现了:该sort 的东西。如果网站未提供weight,则最后写入。但是,如果它确实提供了一个,则仍然不是根据它来排序,而是根据文件名来排序。
flyx 2012年

4
我相信排序过滤器可能仅适用于输出标记(包裹在{{}}中的内容,而不是{%%})。因此,它可能无法用作for循环上的过滤器。我的评论基于此页面:github.com/Shopify/liquid/wiki/Liquid-for-Designers,它说过滤器用于输出标记。
Owen

Answers:


75

从Jekyll 2.2.0开始,您可以按任何对象属性对对象数组进行排序。您现在可以执行以下操作:

{% assign pages = site.pages | sort:"weight"  %}
<ul>
  {% for p in pages %}
    <li>
      <a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
        {{ p.title }}
      </a>
    </li>
  {% endfor %}
</ul>

与@kikito解决方案相比,可以节省大量的构建时间。

编辑:您必须将排序属性分配为整数weight: 10而不是字符串weight: "10"

将排序属性分配为字符串将最终得到字符串排序,例如“ 1、10、11、2、20,...”


1
不适用于我(Jekyll 2.4.0)。我如上所述在页面中定义了weight属性,但是排序似乎忽略了它。
eyettea 2014年

2
@eyetea你是对的。我们需要先做一个作业。我已经编辑了代码,并且可以在Jekyll 2.4.0上运行。;-)
David Jacquel 2014年

谢谢您的帮助。我还编辑了代码,并删除了第二个排序过滤器,因为看起来不再需要它了。
eyettea 2014年

你是对的。我自己进行了修改,因为您建议的修改被3个用户拒绝了?
David Jacquel 2014年

奇怪,我刚刚删除了“ | sort:weight” ...为什么会被拒绝?无论如何,问题解决了。
eyettea 2014年

36

您唯一的选择似乎是使用双循环。

<ul>
{% for weight in (1..10) %}
  {% for p in site.pages %}
    {% if p.weight == weight %}
      <li>
        <a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
          {{ p.title }}
        </a>
      </li>
    {% endif %}
  {% endfor %}
{% endfor %}
</ul>

看起来很丑,它应该可以工作。如果您还有没有权重的页面,则必须{% unless p.weight %}在当前内部循环之前/之后添加一个额外的内部循环。


1
大声笑。我想您可以通过将所有内容压缩为一行代码来减少这种情况。不幸的是,liquid没有{%- %}像erb这样的空行折叠前缀。
kikito 2012年

3
只是补充:用(1..site.pages.size)替换(1..10)使此循环尽可能短,并且无论您有多少页都可以使用。感谢愚蠢而又非常聪明的黑客:)
Markus Amalthea Magnuson

(1..10)此代码上的@MarkusAmaltheaMagnuson表示可能的权重。可以将其替换(1..MAX_WEIGHT)以使其更清晰(并在其他位置定义MAX_WEIGHT,例如在常量文件中)。
kikito

1
这对我
有用

这是关于Jekyll / static-generation的最好的事情之一-可能是“丑陋的”,它只运行一次,不会影响用户体验或服务器负载。不错的解决方案!
Paul Ferrett 2014年

29

以下解决方案可在Github上使用(不需要插件):

{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
  <li><a href="{{node.url}}">{{node.title}}</a></li>
{% endfor %}

上面的代码片段按文件名name对页面进行排序(Page对象的属性是从文件名派生的)。我重命名了文件以符合我的期望顺序:00-index.md01-about.md–和presto!页面是有序的。

一个陷阱是,这些数字前缀出现在URL中,对于大多数页面而言,它们看起来很尴尬,而对于00-index.html来说,这是一个真正的问题。Permalilnks进行救援:

---
layout: default
title: News
permalink: "index.html"
---

PS我想变得聪明,并添加自定义属性仅用于排序。不幸的是,自定义属性不能作为Page类上的方法访问,因此不能用于排序:

{% assign sorted_pages = site.pages | sort:"weight" %} #bummer

您好先生,值得一枚奖牌。我只是想在本地生成我的网站,然后将静态HTML推送到GitHub,以便能够使用插件来做到这一点。
亚当B

按完整文件路径排序是否可行?即{% assign sorted_pages = site.pages | sort:"path" %}00-directory/00-file.md会来01-anotherDir/00-anotherFile.md
布雷纳2014年

确实有效!非常感谢!我已经为此战斗了好几天。
brenna 2014年

2
嗨@Wojteksort:"weight"刚刚为我工作,菲。顺便说一下,感谢您的出色解决方案。
destan 2014年

1
按重量排序确实有效!请参阅下面的答案也许在Jekyll中已更改。
wedi 2014年

15

我编写了一个简单的Jekyll插件来解决此问题:

  1. sorted_for.rbhttps://gist.github.com/3765912复制到_pluginsJekyll项目的子目录:

    module Jekyll
      class SortedForTag < Liquid::For
        def render(context)
          sorted_collection = context[@collection_name].dup
          sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] }
    
          sorted_collection_name = "#{@collection_name}_sorted".sub('.', '_')
          context[sorted_collection_name] = sorted_collection
          @collection_name = sorted_collection_name
    
          super
        end
    
        def end_tag
          'endsorted_for'
        end
      end
    end
    
    Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
    
  2. 使用标记sorted_for而不是forsort_by:property参数按给定属性排序。您也可以reversed像原始内容一样添加for
  3. 不要忘记使用其他结束标签endsorted_for

在您的情况下,用法如下所示:

<ul>
  {% sorted_for p in site.pages sort_by:weight %}
    <li>
      <a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
        {{ p.title }}
      </a>
    </li>
  {% endsorted_for %}
</ul>

4
遗憾的是您不能在GitHub页面上使用自定义插件…:-\
Paul Wagland 2013年

3
很好,谢谢分享。补充一点:如果不是所有项目都具有指定的属性,则可以更改sort_by!调用以忽略这些项目:(sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] || 0 }如果需要,可以将0替换为无穷大)。
MisterMetaphor

10

最简单的解决方案是在页面的文件名前添加一个索引,如下所示:

00-home.html 01-services.html 02-page3.html

页面按文件名排序。但是,现在您将拥有难看的网址。

在yaml前题部分中,您可以通过设置永久链接变量来覆盖生成的url。

例如:

---
layout: default
permalink: index.html
---

好一个!仍然是hack,但是比其他答案要简单得多。
flyx 2012年

如果将您的网站推送到github页面,请务必谨慎。由于某种原因,订购会因此混乱。参见:github.com/plusjade/jekyll-bootstrap/issues/...
克里斯蒂安·

10

简单的解决方案:

首先分配一个已排序的数组,site.pages然后在该数组上运行for循环。

您的代码如下所示:

{% assign links = site.pages | sort: 'weight' %}
{% for p in links %}
  <li>
    <a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
      {{ p.title }}
    </a>
  </li>
{% endfor %}

这在我的导航栏中起作用,_include它很简单:

<section id="navbar">
    <nav>
        {% assign tabs = site.pages | sort: 'weight' %}
        {% for p in tabs %}
            <span class="navitem"><a href="{{ p.url }}">{{ p.title }}</a></span>
        {% endfor %}
    </nav>
</section>

突然,这开始在_post页面上引发错误:Liquid Exception: comparison of Hash with Hash failed in _posts/...
sdmeyers

这是最干净的解决方案。只是有一个小错误-排序键应以字符串形式给出,即sort:'weight'。更新了示例代码。
2014年

这里提到并解决上述问题。不过,更新运行GitHub页面的版本可能需要一段时间。
2014年

5

我已经使用发电机解决了这个问题。生成器遍历页面,获取导航数据,对其进行排序并将其推回站点配置。Liquid可以从那里检索数据并显示它。它还负责隐藏和显示项目。

考虑以下页面片段:

---
navigation:
  title: Page name
  weight: 100
  show: true
---
content.

导航使用以下Liquid片段呈现:

{% for p in site.navigation %}
<li> 
    <a  {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}

将以下代码放在_plugins文件夹中的文件中:

module Jekyll

  class SiteNavigation < Jekyll::Generator
    safe true
    priority :lowest

    def generate(site)

        # First remove all invisible items (default: nil = show in nav)
        sorted = []
        site.pages.each do |page|
          sorted << page if page.data["navigation"]["show"] != false
        end

        # Then sort em according to weight
        sorted = sorted.sort{ |a,b| a.data["navigation"]["weight"] <=> b.data["navigation"]["weight"] } 

        # Debug info.
        puts "Sorted resulting navigation:  (use site.config['sorted_navigation']) "
        sorted.each do |p|
          puts p.inspect 
        end

        # Access this in Liquid using: site.navigation
        site.config["navigation"] = sorted
    end
  end
end

因为我对Jekyll和Ruby还是很陌生,所以花了很多时间弄清楚这个问题,因此如果有人可以对此进行改进,那将是很棒的。


1

我可以将以下代码与Jekyll / Liquid配合使用以符合您对类别的要求:

  • 创建指向所有现有页面的链接,
  • 按权重排序(也适用于按类别排序),
  • 突出显示当前页面。

在它们之上,它还显示了帖子数。无需任何插件即可完成所有操作。

<ul class="topics">
{% capture tags %}
    {% for tag in site.categories %}
        {{ tag[0] }}
    {% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
    {% for tag in sortedtags %}
    <li class="topic-header"><b>{{ tag }} ({{ site.categories[tag] | size }} topics)</b>
        <ul class='subnavlist'>
        {% assign posts = site.categories[tag] | sort:"weight" %}
        {% for post in posts %}
            <li class='recipe {% if post.url == page.url %}active{% endif %}'>
            <a href="https://stackoverflow.com/{{ site.github.project_title }}{{ post.url }}">{{ post.title }}</a>
            </li>
        {% endfor %}
        </ul>
    </li>
    {% endfor %}
</ul>

在我们的网络页面上检查操作。您可以单击帖子以突出显示导航,也可以单击给定的链接将您带到分配其权重的源页面。


0

如果您要尝试按重量和标记排序并将数字限制为10,请执行以下代码:

{% assign counter = '0' %}
{% assign pages = site.pages | sort: "weight"  %}
{% for page in pages %}
{% for tag in page.tags %}
{% if tag == "Getting Started" and counter < '9' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li><a href="{{ page.permalink | prepend: site.baseurl }}">{{page.title}}</a></li>
{% endif %}
{% endfor %}
{% endfor %} 

-1

@kikito的上述解决方案也对我有用。我只添加了几行内容来删除导航中没有重量的页面并摆脱空白:

<nav>
  <ul>
    {% for weight in (1..5) %}
      {% unless p.weight %}
        {% for p in site.pages %}
          {% if p.weight == weight %}
            {% if p.url == page.url %}
              <li>{{ p.title }}</li>
            {% else %}
              <li><a href="{{ p.url }}" title="{{ p.title }}">{{ p.title }}</a></li>
            {% endif %}
          {% endif %}
        {% endfor %}
      {% endunless %}
    {% endfor %}
  </ul>
</nav>

@WingLeong我没有做任何测试,但这是对我有用的。
木星
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.