Sorted navigation menu with Jekyll and Liquid

后端 未结 10 1963
清酒与你
清酒与你 2020-12-07 11:57

I\'m constructing a static site (no blog) with Jekyll/Liquid. I want it to have an auto-generated navigation menu that lists all existing pages and highlight the current pag

相关标签:
10条回答
  • 2020-12-07 12:38

    Below solution works on Github (doesn't require a plugin):

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

    Above snippet sorts pages by file name (name attribute on Page object is derived from file name). I renamed files to match my desired order: 00-index.md, 01-about.md – and presto! Pages are ordered.

    One gotcha is that those number prefixes end up in the URLs, which looks awkward for most pages and is a real problem in with 00-index.html. Permalilnks to the rescue:

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

    P.S. I wanted to be clever and add custom attributes just for sorting. Unfortunately custom attributes are not accessible as methods on Page class and thus can't be used for sorting:

    {% assign sorted_pages = site.pages | sort:"weight" %} #bummer
    
    0 讨论(0)
  • 2020-12-07 12:38

    I can get the code below works on with Jekyll/Liquid match to your requirement with category:

    • creates links to all existing pages,
    • sorted by weight (works as well on sorting per category),
    • highlight the current page.

    On top of them it shows also number of post. All is done without any plug-in.

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

    Check it on action on our networking page. You may click a post to highlight the navigation, as well a given link to bring you to the source page where their weight is assigned.

    0 讨论(0)
  • 2020-12-07 12:40

    If you're trying to sort by weight and by tag and limit the number to 10, here's code to do it:

    {% 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 %} 
    
    0 讨论(0)
  • 2020-12-07 12:45

    Since Jekyll 2.2.0 you can sort an array of objects by any object property. You can now do :

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

    And save a lot of build time compared to @kikito solution.

    edit: You MUST assign your sorting property as an integer weight: 10 and not as a string weight: "10".

    Assigning sorting properties as string will ends up in a a string sort like "1, 10, 11, 2, 20, ..."

    0 讨论(0)
  • 2020-12-07 12:45

    Your only option seems to be using a double loop.

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

    Ugly as it is, it should work. If you also have pages without a weight, you will have to include an additional internal loop just doing {% unless p.weight %} before/after the current internal one.

    0 讨论(0)
  • 2020-12-07 12:45

    I've written a simple Jekyll plugin to solve this issue:

    1. Copy sorted_for.rb from https://gist.github.com/3765912 to _plugins subdirectory of your Jekyll project:

      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. Use tag sorted_for instead of for with sort_by:property parameter to sort by given property. You can also add reversed just like the original for.
    3. Don't forget to use different end tag endsorted_for.

    In your case the usage look like this:

    <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>
    
    0 讨论(0)
提交回复
热议问题