【问题标题】:Sorted navigation menu with Jekyll and Liquid带有 Jekyll 和 Liquid 的排序导航菜单
【发布时间】:2012-02-21 14:37:24
【问题描述】:

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

---
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,则最后写入。但是如果确实提供了一个,仍然不是按照它排序,而是按照文件名排序。
  • 我相信排序过滤器可能只适用于输出标记(包装在 {{ }} 中的东西,而不是 {% %})。因此,它可能无法用作 for 循环上的过滤器。我的评论基于此页面:github.com/Shopify/liquid/wiki/Liquid-for-Designers,它说过滤器用于输出标记。

标签: jekyll liquid


【解决方案1】:

从 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, ...”

【讨论】:

  • 对我不起作用(Jekyll 2.4.0)。如上所述,我在页面中定义了 weight 属性,但排序似乎忽略了它。
  • @eyetea 你是对的。我们需要先做一个作业。我编辑了我的代码,它适用于 Jekyll 2.4.0。 ;-)
  • 感谢您的帮助。我还编辑了代码并删除了第二个排序过滤器,因为它看起来不再需要了。
  • 你是对的。我自己编辑了它,因为您建议的编辑被 3 个用户拒绝了???
  • 奇怪,我刚刚删除了“| sort: weight”……为什么会被拒绝?总之,问题解决了。
【解决方案2】:

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

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

【讨论】:

  • 哈哈。我想如果这是一个问题,你可以通过将所有内容压缩成一行代码来减少它。不幸的是,liquid 没有 {%- %} 前缀来折叠像 erb 这样的空行。
  • 只是一个补充:将 (1..10) 替换为 (1..site.pages.size) 使此循环尽可能短,并且无论您拥有多少页都可以正常工作。感谢一个愚蠢但非常聪明的黑客:)
  • @MarkusAmaltheaMagnuson 此代码上的(1..10) 表示可能的权重。可以将其替换为(1..MAX_WEIGHT) 以使其更加清晰(并在其他地方定义 MAX_WEIGHT,例如在常量文件中)。
  • 这对我有用,除了“活动”类需要在
  • 而不是
  • 这是 Jekyll/static-generation 最好的事情之一——尽管它可能很“丑陋”,但它只运行一次,不会影响用户体验或服务器负载。 不错的解决方案!
  • 【解决方案3】:

    以下解决方案适用于 Github(不需要插件):

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

    sn-p 以上按文件名对页面进行排序(Page 对象上的name 属性源自文件名)。我重命名了文件以匹配我想要的顺序:00-index.md01-about.md - 并且很快!页面是有序的。

    一个问题是这些数字前缀最终出现在 URL 中,这对于大多数页面来说看起来很尴尬,并且在 00-index.html 中是一个真正的问题。永久链接到救援:

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

    附:我想聪明点,添加自定义属性只是为了排序。不幸的是,自定义属性不能作为 Page 类的方法访问,因此不能用于排序:

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

    【讨论】:

    • 你好先生应该获得奖章。我正要求助于在本地生成我的网站并将静态 HTML 推送到 GitHub 以便能够使用插件来执行此操作。
    • 这适用于按完整文件路径排序吗?即{% assign sorted_pages = site.pages | sort:"path" %}00-directory/00-file.md 将在01-anotherDir/00-anotherFile.md 之前出现
    • 它确实有效!非常感谢!我已经为此奋斗了好几天。
    • 嗨 @Wojtek sort:"weight" 对我有用,仅供参考。顺便感谢您提供的出色解决方案。
    • 按重量排序确实有效! See answer below 也许 Jekyll 改变了。
    【解决方案4】:

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

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

      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 参数按给定属性排序。你也可以像原来的for一样添加reversed
    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>
    

    【讨论】:

    • 可惜不能在 GitHub 页面上使用自定义插件... :-\
    • 很好,谢谢分享。只是一点点补充:如果不是所有项目都具有指定的属性,您可以更改 sort_by! 调用以忽略这些项目:sorted_collection.sort_by! { |i| i.to_liquid[@attributes['sort_by']] || 0 }(如果您想要它,请将 0 替换为无穷大)。跨度>
    【解决方案5】:

    最简单的解决方案是在页面的文件名前加上这样的索引:

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

    页面按文件名排序。但是,现在你的网址会很丑。

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

    例如:

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

    【讨论】:

    • 不错的一个!仍然是一个 hack,但比其他答案简单得多。
    • 如果您将网站推送到 github 页面,请注意这一点。由于某种原因,订购会被搞砸。另见:github.com/plusjade/jekyll-bootstrap/issues/…
    【解决方案6】:

    简单的解决方案:

    首先分配一个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/...
    • 这是最干净的解决方案;只是一个小错误——排序键应该作为字符串给出,即排序:'重量'。更新了示例代码。
    • 上述问题已被提及并解决here。不过,运行 GitHub 页面的版本可能需要一段时间才能更新。
    【解决方案7】:

    我已经使用生成器解决了这个问题。生成器遍历页面,获取导航数据,对其进行排序并将其推送回站点配置。 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 还很陌生,所以我花了很长时间才弄清楚这一点,所以如果有人可以改进这一点,那就太好了。

    【讨论】:

      【解决方案8】:

      我可以让下面的代码与 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="/{{ site.github.project_title }}{{ post.url }}">{{ post.title }}</a>
                  </li>
              {% endfor %}
              </ul>
          </li>
          {% endfor %}
      </ul>
      

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

      【讨论】:

        【解决方案9】:

        如果您尝试按重量和标签排序并将数量限制为 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 %} 
        

        【讨论】:

          【解决方案10】:

          @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 我没有做任何测试,但这对我有用。
          猜你喜欢
          相关资源
          最近更新 更多
          热门标签