Creating a menu

Creating a menu with previous and next links.

Sorting a collection

There are many ways to sort a collection of posts, some of them provided out-of-the-box by Eleventy see Eleventy#sorting documentation page. . However, 11ty Tips doesn’t use the formated date front matter property but a specific one, named rank_n it’s an integer positive number , allowing to sort a posts collection according a unique value regardless of the date you could have multiple posts with the same date, unless you use a full date with hours, minutes and seconds… This rank_n should therefore be a unique index because it is used to retrieve the previous and next pages of any given post in the menu list; however, a duplicate rank_n index doesn’t cause any disturbance when retrieving those links (see infra). .

11tyTips/source/make/11ty/collections.js
Prism

const sort_o =
{
//> Sort a tag collection
//> according to rank_n front matter property
  sortByRank__a: ( collection_a, tag_s ) =>
  {
    return collection_a
      .getFilteredByTag( tag_s )
      .sort( ( current_o, other_o ) => current_o.data.rank_n - other_o.data.rank_n )
  }
}

module.exports = make_o => { make_o.tag_a.forEach( tag_o => make_o.addCollection( tag_o.tag_s, collection_a => sort_o[tag_o.sort_f]( collection_a, tag_o.tag_s ) ) ) }

Listing posts as an HTML fragment

To display the global menu in every page of the site, an HTML fragment is built with a template listing all pages as links in an ordered list, each one having its page rank_n front matter property recorded as a data-rank attribute.

The main loop of the menu template iterates thru the posts collection previously sorted and add every useful data that we want to display in the menu itself or as a clue of the previous and next pages: permalink, rank_n, title_s, etc.

11tyTips/source/matrix/menu.njk
Prism

{%- set _collection_s = A_o.COLLECTION_s %}
{# .... #}
{%- set _menu_list_s %}
  
    {% for _item_o in collections[_collection_s] %} {% if _item_o.data.tags == _collection_s %} {% set _link_s = _item_o.data.permalink.slice( 0, _html_n ) %}
  1. {{pad__s( _item_o.data.rank_n )}} {{_item_o.data.title_s}} {{_item_o.data.abstract_s}}
  2. {% endif %} {% endfor %}
{% endset -%}

Previous and next posts links are much less difficult to retrieve on the client side than at build time. For that reason, the menu template doesn’t try to create a double linked list but instead delegates the work to a JavaScript function run in the browser. For any post page displayed by the browser, we have in the menu HTML fragment built by the template all necessary data about the preceding and following pages relative to the current one when they exist: the first page in the menu list has no previous page and the last one no next page! .

For that we have to search the DOM for the nodes having a data-rank attribute with values surrounding that one of the current page.

11tyTips/source/matrix/assets/scripts/js/parts/_link_page_.js
Prism

//> retrieve previous + next pages info
const linkNear__o = link_s =>
{
  const list_e = document.querySelector( `[data--="menu_list"]` )
  const extension_n = '.html'.length
  const location_s = window.location.pathname.slice( 1, -extension_n )  //: trim '/' at start
  const list_a = document.querySelectorAll( `[data--="menu_list"] > li` )
  if ( !list_a ) return     //: undefined
  const list_n = list_a.length
  const current_e = list_e.querySelector( `[data-link="${location_s}"]` )
  const rank_n = +current_e.getAttribute( 'data-rank' )
  const near_n = ( link_s === 'link_previous' ) ? rank_n - 1 : rank_n + 1
  if ( near_n < 1 || near_n > list_n ) return     //: undefined
  const near_e = list_e.querySelector( `[data-rank="${near_n}"]` )
  if ( !near_e ) return     //: undefined
  const a_e = near_e.querySelector( `span > a` )
  if ( !a_e ) return     //: undefined
  const span_e = near_e.querySelector( `span[data--="note_content"]` )
  if ( !a_e ) return     //: undefined
  return {
    link_s:     near_e.getAttribute( 'data-link' ),
    title_s:    a_e.innerHTML,
    //?? subtitle_s: '',
    abstract_s: span_e.innerHTML,
  }
}

A link is only a link and doesn’t convey a lot of meaning by itself apart its URL the href attribute . Unveiling the title and some other pieces of data before fetching a previous or next post is a much more useful help. The data previously retrieved in the surrounding links of the current page are there to be used and we can display them as we like bellow the navigation bar at the top of the page .

11tyTips/source/matrix/assets/scripts/js/parts/_link_page_.js
Prism

//> insert in DOM previous + next pages info
//> show/hide previous + next pages info block
const linkNear__v = ( event_s, link_e ) =>
{
  if ( link_e === null ) return
  if ( event_s === 'mouseenter' )
  {
    const link_s = link_e.getAttribute( 'data--' )
    let title_s
    let abstract_s
    const near_o = linkNear__o( link_s )
    if ( near_o !== undefined )
    {
      title_s = `${near_o.title_s} ⤴`
      abstract_s = `${near_o.abstract_s}`
    }
    else
    {
      title_s = 'No more tip'
      abstract_s = ''
    }
    document.querySelector( '[data--="link_title"]' ).innerHTML = title_s
    document.querySelector( '[data--="link_abstract"]' ).innerHTML = abstract_s
}
  document.querySelector( '[data--="link_info"]' )
    .classList.toggle( 'retract' )
}

Comments