Where to Discuss?

Local Group

Preface

Goal: Add indicator, and putting all pagination part together.

Source Code

You can download the source code of this article here.

Extract and run on CLI using $ npm install.


1: Source

I respect copyright. Most of the code below, especially middle pagination, copied and pasted from:

The rest is my modification.


2: Prepare

This step is required.

Configuration

As usual in Hexo - Pagination - Intro.

Layout: EJS Index

Consider use pagination/04-indicator layout, in index.ejs

  <section class="section">
    partial('pagination/04-indicator')
  </section>

Layout: EJS Index for Screenshot

I omit the section tag for tutorial purpose, so that I can have enough width, for all pagination example, in 480px screen.

    partial('pagination/04-indicator')

Layout: Minimal Pagination Code

You should have this minimal code, before you begin.

<nav class="pagination is-small is-centered"
     role="navigation" aria-label="pagination">
  <% if (page.total > 1) { %>

    <ul class="pagination-list">
    <% if (page.total > max_links) { %>
      ...
    <% } %>
    </ul>

  <% } %>
</nav>

Javascript: EJS Pagination Indicator

The javascript part is remain the same.


3: Preview: General

Structure

This consist of at least seven parts:

  • Previous Page: «

  • First Page: always 1

  • Left Indicator

  • Middle Pagination: Glenn McComb

  • Right Indicator

  • Last Page: always the same number

  • Next Page: »

We will not discuss about Middle Pagination, as it has already been discussed in previous article.

HTML Preview

The HTML that we want to achieve is similar as below.

<nav class="pagination is-small is-centered" ...>

    <ul class="pagination-list">

      <!-- Previous Page. -->
      <li class="blog-previous"><a class="pagination-previous" ...>«&nbsp;</a></li>

      <!-- First Page. -->
      <li><a class="pagination-link" ...>1</a></li>

      <!-- Early (More Pages) Indicator. -->
      <li><span class="pagination-ellipsis">&hellip;</span></li>

      <li><a class="pagination-link" ...>3</a></li>
      <li><a class="pagination-link" ...>4</a></li>
      <li><a class="pagination-link is-current" >5</a></li>
      <li><a class="pagination-link" ...>6</a></li>
      <li><a class="pagination-link" ...>7</a></li>

      <!-- Late (More Pages) Indicator. -->
      <li><span class="pagination-ellipsis">&hellip;</span></li>

      <!-- Last Page. -->
      <li><a class="pagination-link"  ...>9</a></li>

      <!-- Next Page. -->
      <li class="blog-next"><a class="pagination-next" ...>&nbsp;»</a></li>

    </ul>

</nav>

Small Preview

This is the complete version.

Hexo Pagination: Combined Animation

Wide Preview

Wide version, in responsive context, is slightly different.

Hexo Pagination: Preview Wide

We will use responsive CSS later to achieve this effect.

Layout: Pagination Code Skeleton

As usual, the skeleton, to show the complexity.

// Variable Initialization.

<nav role="navigation" aria-label="pagination">
  <% if (page.total > 1) { %>
    <ul class="pagination-list">

    // Previous Page.
    // First Page.
    // Early (More Pages) Indicator.

    <% 
  var cursor; 
  for (cursor = 1; cursor <= page.total; cursor++) { 
    // Variable Initialization

    if (page.total > max_links) {
      // Complex page numbers.

      // Check between these three:
      // * Lower limit pages, or
      // * Upper limit pages, or
      // * Middle pages.

    } else {
      // Simple page numbers.
      ...
    }

    // Show Pager.
    ...

    <% } %>

    // Late (More Pages) Indicator.
    // Last Page.
    // Next Page.

    </ul>
  <% } %>
</nav>

Each Pagination

Consider again, have a look at the animation above, frame by frame.

We have from first page (1), to last page (9).

Hexo Pagination: Indicator Page 1

Hexo Pagination: Indicator Page 2

Hexo Pagination: Indicator Page 3

Hexo Pagination: Indicator Page 4

Hexo Pagination: Indicator Page 5

Hexo Pagination: Indicator Page 6

Hexo Pagination: Indicator Page 7

Hexo Pagination: Indicator Page 8

Hexo Pagination: Indicator Page 9


4: Variables: Initialization

Consider refresh our memory, about our variables initialization.

Javascript: EJS Pagination Indicator

The javascript part is remain similar. With a slight change, that this pagination is now capable of, handling many kind of standard pages [archive, category, tag].

/*
* Helper function
*/

function pagination_url(number) {
  var path;

  // default for index
  var path = config.index_generator.path;
  
  // dirty quick fix, avoid double (//)
  if (path=='/') { path = '' }
  
  if (is_archive()){
    path = '/' + config.archive_dir;

    if (is_month()){
      // trailing zero
      var month = ( page.month < 10 ? '0' + page.month : page.month );
      path += '/' + page.year + '/' + month;
    } else if (is_year()){
      path += '/' + page.year;
    }
  } else if (is_category()){
    path = '/' + config.category_dir + '/' + page.category;
  } else if (is_tag()){
    path = '/' + config.tag_dir + '/' + page.tag;
  }

  if (number>1) {
      path = path + '/' + config.pagination_dir + '/' + number;
  }

  return url_for(path);
}

/* 
* Pagination links 
* https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
* Adjacent: Number of links either side of the current page
*/

const adjacent_links = 2;
var current          = page.current;
var max_links        = (adjacent_links * 2) + 1;
var lower_limit      = 1 + adjacent_links;
var upper_limit      = page.total - adjacent_links;
  • .

5: Navigation: Previous and Next

It is similar to our simple pagination. Except that, now we use dictionary.

      <!-- Previous Page. -->
      <li class="blog-previous">
      <% if (page.prev_link) { %>
        <a class="pagination-previous"
           href="<%= url_for(page.prev_link) %>" 
           rel="prev">&laquo;&nbsp;</a>
      <% } else { %>
        <a class="pagination-previous"
           title="This is the first page"
           disabled>&laquo;&nbsp;</a>
      <% } %>
      </li>
      <!-- Next Page. -->
      <li class="blog-next">
      <% if (page.next_link) { %>
        <a class="pagination-next"
           href="<%= url_for(page.next_link) %>" 
           rel="next">&nbsp;&raquo;</a>
      <% } else { %>
        <a class="pagination-next"
           title="This is the last page"
           disabled>&nbsp;&raquo;</a>
      <% } %>
      </li>

6: Navigation: First and Last

It is different to our simple pagination. Although it is based on the same logic.

This will not be shown, if it is already be shown middle pagination.

    <% if (page.total > max_links) { %>
      <% if (current - adjacent_links > 1) { %>
      <!-- First Page. -->
      <li>
        <a href="<%= pagination_url(1) %>"
           class="pagination-link" 
           aria-label="Goto page 1"
          >1</a>
      </li>
      <% } %>
    <% } %>

This will not be shown, if it is already be shown middle pagination.

    <% if (page.total > max_links) { %>
      <% if (current + adjacent_links < page.total) { %>
      <!-- Last Page. -->
      <li>
        <a href="<%= pagination_url(page.total) %>"
           class="pagination-link" 
           aria-label="Goto page <%= page.total %>"
          ><%= page.total %></a>
      </li>
      <% } %>
    <% } %>

7: Indicator: Left and Right

These will only be shown, only if necessary.

Indicator: Left

    <% if (page.total > max_links) { %>
      <% if (current - adjacent_links > 2) { %>
      <!-- Early (More Pages) Indicator. -->
      <li>
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>
    <% } %>

Indicator: Right

    <% if (page.total > max_links) { %>
      <% if (current + adjacent_links < page.total - 1) { %>
      <!-- Late (More Pages) Indicator. -->
      <li>
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>
    <% } %>

8: Combined Code

It is about the right time to put all the code together.

Hexo Pagination: Combined Animation

<%
/*
* Helper function
*/

function pagination_url(number) {
  var path;

  // default for index
  var path = config.index_generator.path;
  
  // dirty quick fix, avoid double (//)
  if (path=='/') { path = '' }
  
  if (is_archive()){
    path = '/' + config.archive_dir;

    if (is_month()){
      // trailing zero
      var month = ( page.month < 10 ? '0' + page.month : page.month );
      path += '/' + page.year + '/' + month;
    } else if (is_year()){
      path += '/' + page.year;
    }
  } else if (is_category()){
    path = '/' + config.category_dir + '/' + page.category;
  } else if (is_tag()){
    path = '/' + config.tag_dir + '/' + page.tag;
  }

  if (number>1) {
      path = path + '/' + config.pagination_dir + '/' + number;
  }

  return url_for(path);
}

/* 
* Pagination links 
* https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
* Adjacent: Number of links either side of the current page
*/

var current        = page.current;
var adjacent_links = 2;
var max_links      = (adjacent_links * 2) + 1;
var lower_limit    = 1 + adjacent_links;
var upper_limit    = page.total - adjacent_links;
%>

<nav class="pagination is-small is-centered"
     role="navigation" aria-label="pagination">
  <% if (page.total > 1) { %>

    <ul class="pagination-list">

      <!-- Previous Page. -->
      <li class="blog-previous">
      <% if (page.prev_link) { %>
        <a class="pagination-previous"
           href="<%= url_for(page.prev_link) %>" 
           rel="prev">&laquo;&nbsp;</a>
      <% } else { %>
        <a class="pagination-previous"
           title="This is the first page"
           disabled>&laquo;&nbsp;</a>
      <% } %>
      </li>

    <% if (page.total > max_links) { %>
      <% if (current - adjacent_links > 1) { %>
      <!-- First Page. -->
      <li>
        <a href="<%= pagination_url(1) %>"
           class="pagination-link" 
           aria-label="Goto page 1"
          >1</a>
      </li>
      <% } %>

      <% if (current - adjacent_links > 2) { %>
      <!-- Early (More Pages) Indicator. -->
      <li>
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>
    <% } %>

    <% 
  var cursor; 
  for (cursor = 1; cursor <= page.total; cursor++) { 
    var show_cursor_flag = false;


    if (page.total > max_links) {
      // Complex page numbers.

      if (current <= lower_limit) {
        // Lower limit pages.
        // If the user is on a page which is in the lower limit.
        if (cursor <= max_links) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else if (current >= upper_limit) {
        // Upper limit pages.
        // If the user is on a page which is in the upper limit.
        if (cursor > (page.total - max_links)) {
          // If the current loop page is less than max_links.
          show_cursor_flag = true;
        }
      } else {
        // Middle pages.
        if ( (cursor >= current - adjacent_links) 
        &&   (cursor <= current + adjacent_links) ) {
          show_cursor_flag = true;
        }
      }
    } else {
      // Simple page numbers.
      show_cursor_flag = true;
    }

    // Show Pager.
    if (show_cursor_flag) { %>
      <li>
        <% if (current != cursor) { %>
        <a href="<%= pagination_url(cursor) %>"
           class="pagination-link" 
           aria-label="Goto page <%= cursor %>">
          <%= cursor %>
        </a>
        <% } else { %>
        <a class="pagination-link is-current" 
           aria-label="Page <%= current %>">
          <%= current %>
        </a>
        <% } %>
      </li>
      <% } /* if */ %>
    <% } /* for */ %>

    <% if (page.total > max_links) { %>
      <% if (current + adjacent_links < page.total - 1) { %>
      <!-- Late (More Pages) Indicator. -->
      <li>
        <span class="pagination-ellipsis">&hellip;</span>
      </li>
      <% } %>

      <% if (current + adjacent_links < page.total) { %>
      <!-- Last Page. -->
      <li>
        <a href="<%= pagination_url(page.total) %>"
           class="pagination-link" 
           aria-label="Goto page <%= page.total %>"
          ><%= page.total %></a>
      </li>
      <% } %>
    <% } %>

      <!-- Next Page. -->
      <li class="blog-next">
      <% if (page.next_link) { %>
        <a class="pagination-next"
           href="<%= url_for(page.next_link) %>" 
           rel="next">&nbsp;&raquo;</a>
      <% } else { %>
        <a class="pagination-next"
           title="This is the last page"
           disabled>&nbsp;&raquo;</a>
      <% } %>
      </li>

    </ul>
  <% } %>
</nav>
  • .

9: Responsive: Simple

Before we continue to the next pagination article about responsive, consider having this very simple example using bulma.

Hexo Pagination: Preview Wide

This will show the word previous and next for tablet page.

SASS: Custom Pagination

I’m using Bulma breakpoints.

+tablet
  li.blog-previous a:after
    content: " previous"
  li.blog-next a:before
    content: "next "

SASS: Bulma Breakpoint Variables.

Bulma 0.7 breakpoints are defined as below.

  • themes/tutor-05/sass/vendors/bulma/utilities/initial-variables.sass
// The container horizontal gap,
// which acts as the offset for breakpoints

$gap        : 64px !default
$tablet     : 769px !default
$desktop    : 960px + (2 * $gap) !default
$widescreen : 1152px + (2 * $gap) !default
$fullhd     : 1344px + (2 * $gap) !default

What is Next ?

Consider continue reading [ Hexo - Pagination - Responsive ]. There are, some interesting topic about Pagination in Hexo.

Thank you for reading.