Preface
Goal: Explaining Glenn McComb Pagination using Math and Table.
Preview
This is what we want to achieve in this tutorial.
Notice that this is just the middle pagination. We will combine it later with indicator to make it pretty.
The later figure will be discussed in the next article.
More Recent Code
I have made a better code with Bulma
utilizing site.pagination.permalink
which you can examine at:
1: Prepare
Artefact that we need.
Source
I respect copyright. The code below copied, and pasted from:
I made a slight modification. But of course the logic remain the same.
I mostly write it down in my blog, so I can find my custom code easily.
Includes: Pagination: Adjacent
Create an empty artefact. Source code used in this tutorial, is available at this repository:
- /_includes/pagination/03-adjacent.html
Pages: Blog List
Set this blog list page:
- /pages/index.html : github.com/…/pages/index.html.
{% include pagination/03-adjacent.html %}
How Does It Works ?
It is basically just mark every number with flag. If the flag is true, the page will be shown, and it will not be shown if the flag value is false.
{% if total_pages > 1 %}
<ul class="pagination justify-content-center">
<!-- Page numbers. -->
{% for page in (1..total_pages) %}
<!-- Flag Calculation -->
<!-- Show Pager: Output Here -->
{% endfor %}
</ul>
{% endif %}
Skeleton
To show how complex it is. This is just skeleton, as we will discuss this later.
- /_includes/pagination/03-adjacent.html : gitlab.com/…/partials/pagination/03-adjacent.html.
{% capture spaceless %}
<!-- Variable Initialization -->
{% endcapture %}
<nav aria-label="Page navigation">
{% if total_pages > 1 %}
<ul class="pagination justify-content-center">
<!-- Page numbers. -->
{% for page in (1..total_pages) %}
{% capture spaceless %}
<!-- Flag Calculation -->
{% if total_pages > link_max %}
<!-- Complex page numbers. -->
<!-- Lower limit pages. -->
<!-- Upper limit pages. -->
<!-- Middle pages. -->
{% else %}
<!-- Simple page numbers. -->
{% endif %}
{% endcapture %}
<!-- Show Pager: Output Here -->
{% endfor %}
</ul>
{% endif %}
</nav>
2: Preview: General
It is not easy to explain by words. Let me explain what we want to achive by using these images below. The blue box, is the active page. We have from first page (1), to last page (10).
Animation: Combined Version
This is the complete version. We will achieve this later.
Animation: Stripped Version
I’m following Glenn McComb code, combined with my own code, and this is the result.
Sample: An Example
Consider get one frame, a sample, because we need an example. This is what we want to achieve in this tutorial.
HTML Preview
The HTML that we want to achieve in this article, is similar as below.
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
<li class="page-item ">
<a class="page-link" href="/demo-jekyll/pages/blog-3">3</a>
</li>
<li class="page-item ">
<a class="page-link" href="/demo-jekyll/pages/blog-4">4</a>
</li>
<li class="page-item active">
<span class="page-link">5</span>
</li>
<li class="page-item ">
<a class="page-link" href="/demo-jekyll/pages/blog-6">6</a>
</li>
<li class="page-item ">
<a class="page-link" href="/demo-jekyll/pages/blog-7">7</a>
</li>
</ul>
</nav>
We will achieve this with Jekyll code.
The Riddle
How do we achieve this ?
4: Math: Basic Algebra
Assumption
Consider our previous example, a blog post contain ten posts. This time with two adjacent link offset. It means, two indicators before selected page, and another two indicators after selected page,
# CONST
$totalPost = 10
$link_offset = 2
Equation
We should manually, do the math.
EQUATION
$link_max = ($link_offset * 2) + 1 = 5
$limit_lower = 1 + $link_offset = 3
$limit_upper = 10 - $link_offset = 8
The real code is shown as below:
{% capture spaceless %}
{% assign total_pages = paginator.total_pages %}
{% assign page_current = paginator.page %}
{% assign link_offset = 2 %}
{% assign link_max = link_offset | times: 2 | plus: 1 %}
{% assign limit_lower = link_offset | plus: 1 %}
{% assign limit_upper = total_pages | minus: link_offset %}
{% endcapture %}
Again, that source above are, ported from:
With some additional variable:
{% capture spaceless %}
{% assign min_lower = link_max %}
{% assign max_upper = total_pages | minus: link_max %}
{% assign lower_offset = page_current | minus: link_offset %}
{% assign upper_offset = page_current | plus: link_offset %}
{% endcapture %}
We calculate these additinal variable earlier, to avoid complexity with limitation of Jekyll Liquid.
Table
The result is on this table below.
# ALGEBRA
+--------------+-------+-------+-------+-------+-------+
| $pagination | 1 | 2 | 5 | 7 | 10 |
+--------------+-------+-------+-------+-------+-------+
| VARIABLE |
| $pages_total | 10 | 5 | 2 | 2 | N/A |
| $link_max | 5 | 5 | 5 | 5 | N/A |
| $limit_lower | 3 | 3 | 3 | 3 | N/A |
| $limit_upper | 8 | 3 | 0 | 0 | N/A |
+--------------+-------+-------+-------+-------+-------+
5: Preview: Detail
Consider, have a look at the pagination below in a stripped down model.
Structure
This will only show one part:
- Middle Pagination: Glenn McComb
Each Pagination
Consider, have a look at the animation above, frame by frame. I’m going to do some reverse engineering, to accomplish better understanding on how this pagination works.
We have from first page (1), to last page (10).
Table
We can rewrite the table with additional rows as below.
+--------------+-------+-------+-------+-------+-------+
| $pagination | 1 | 2 | 5 | 7 | 10 |
+--------------+-------+-------+-------+-------+-------+
| VARIABLE |
| $pages_total | 10 | 5 | 2 | 2 | N/A |
| $link_max | 5 | 5 | 5 | 5 | N/A |
| $limit_lower | 3 | 3 | 3 | 3 | N/A |
| $limit_upper | 8 | 3 | 0 | 0 | N/A |
+--------------+-------+-------+-------+-------+-------+
| MIDDLE PAGINATION |
| $pgNum = 1 | 1..5 | 1..5 | 1..2 | 1..2 |-------+
| $pgNum = 2 | 1..5 | 1..5 | 1..2 | 1..2 | |
| $pgNum = 3 | 1..5 | 1..5 |-------+-------+ |
| $pgNum = 4 | 2..6 | 1..5 | |
| $pgNum = 5 | 3..7 | 1..5 | |
| $pgNum = 6 | 4..8 |-------+ |
| $pgNum = 7 | 5..9 | |
| $pgNum = 8 | 6..10 | |
| $pgNum = 9 | 6..10 | |
| $pgNum = 10 | 6..10 | |
+--------------+-------+-------------------------------+
Flag Calculation
This utilized page_number_flag with complex logic.
{% capture spaceless %}
<!-- Flag Calculation -->
{% assign page_current_flag = false %}
{% if total_pages > link_max %}
<!-- Complex page numbers. -->
<!-- Lower limit pages. -->
<!-- If the user is on a page which is in the lower limit. -->
{% if page_current <= limit_lower %}
<!-- If the current loop page is less than max_links. -->
{% if page <= min_lower %}
{% assign page_current_flag = true %}
{% endif %}
<!-- Upper limit pages. -->
<!-- If the user is on a page which is in the upper limit. -->
{% elsif page_current >= limit_upper %}
<!-- If the current loop page is greater than total pages minus $max_links -->
{% if page > max_upper %}
{% assign page_current_flag = true %}
{% endif %}
<!-- Middle pages. -->
{% else %}
{% if (page >= lower_offset) and (page <= upper_offset) %}
{% assign page_current_flag = true %}
{% endif %}
{% endif %}
{% else %}
<!-- Simple page numbers. -->
{% assign page_current_flag = true %}
{% endif %}
{% endcapture %}
Show Time
And showing the number whenever the page_number_flag comes out. With similar code as previous article.
<!-- Show Pager. -->
{% if page_current_flag == true %}
<li class="page-item {% if page == page_current %} active{% endif %}">
{% if page == page_current %}
<span class="page-link">
{{ page }}
</span>
{% elsif page == 1 %}
<a class="page-link"
href="{{ site.baseurl }}{{ paginate_root }}"
>1</a>
{% else %}
<a class="page-link"
href="{{ site.paginate_path | relative_url | replace: ':num', page }}"
>{{ page }}
</a>
{% endif %}
</li>
{% endif %}
The Same Riddle
How does it works ?
Really! It is confusing.
Don’t be panic!
6: Math: Conditional
Part: Middle Pages
This is already discussed in, so I won’t explain it nomore.
{% capture spaceless %}
{% assign page_current = paginator.page %}
{% assign lower_offset = page_current | minus: link_offset %}
{% assign upper_offset = page_current | plus: link_offset %}
{% endcapture %}
<!-- Middle pages. -->
{% else %}
{% if (page >= lower_offset) and (page <= upper_offset) %}
{% assign page_current_flag = true %}
{% endif %}
{% endif %}
What you need to know is the conditional result in table:
+--------------+-------+
| $pagination | 1 |
| $link_offset | 2 |
| $pages_total | 10 |
+--------------+-------+
| VARIABLE |
| $link_max | 5 |
| $limit_lower | 3 |
| $limit_upper | 8 |
+--------------+-------+-+
| page_number | adjacent|
+--------------+---------+
| $pgNum = 1 | 1..3 |
| $pgNum = 2 | 1..4 |
| $pgNum = 3 | 1..5 |
| $pgNum = 4 | 2..6 |
| $pgNum = 5 | 3..7 |
| $pgNum = 6 | 4..8 |
| $pgNum = 7 | 5..9 |
| $pgNum = 8 | 6..10 |
| $pgNum = 9 | 7..10 |
| $pgNum = 10 | 8..10 |
+--------------+---------+
Part: Lower Limit Pages
Consider stripped more for each part.
{% capture spaceless %}
{% assign link_offset = 2 %}
{% assign link_max = link_offset | times: 2 | plus: 1 %}
{% assign limit_lower = link_offset | plus: 1 %}
{% assign min_lower = link_max %}
{% endcapture %}
<!-- Flag Calculation -->
{% assign page_current_flag = false %}
{% if total_pages > link_max %}
<!-- Complex page numbers. -->
<!-- Lower limit pages. -->
<!-- If the user is on a page which is in the lower limit. -->
{% if page_current <= limit_lower %}
<!-- If the current loop page is less than max_links. -->
{% if page <= min_lower %}
{% assign page_current_flag = true %}
{% endif %}
{% endif %}
{% else %}
<!-- Simple page numbers. -->
{% assign page_current_flag = true %}
{% endif %}
Notice that there is two part of conditional.
-
Outer conditional: result true for the first three row, as defined by $limit_lower.
-
Inner conditional: always result 1..5
Thus, the conditional result in table:
+--------------+-------+-------+--------+
| page_number | lower | l max | result |
+--------------+-------+-------+--------+
| $pgNum = 1 | T | 1..5 | 1..5 |
| $pgNum = 2 | T | 1..5 | 1..5 |
| $pgNum = 3 | T | 1..5 | 1..5 |
| $pgNum = 4 | | 1..5 | |
| $pgNum = 5 | | 1..5 | |
| $pgNum = 6 | | 1..5 | |
| $pgNum = 7 | | 1..5 | |
| $pgNum = 8 | | 1..5 | |
| $pgNum = 9 | | 1..5 | |
| $pgNum = 10 | | 1..5 | |
+--------------+-------+-------+--------+
Combined: All Conditional
Now we have all the logic combined at once.
+--------------+-------+
| $pagination | 1 |
| $link_offset | 2 |
+--------------+-------+
| VARIABLE |
| $pages_total | 10 |
| $max_links | 5 |
| $lower_limit | 3 |
| $upper_limit | 8 |
+--------------+-------+-+-------+-------+-------+-------+
| page_number | adjacent| lower | l max | upper | u max |
+--------------+---------+-------+-------+-------+-------+
| $pgNum = 1 | 1..3 | T | 1..5 | | 6..10 |
| $pgNum = 2 | 1..4 | T | 1..5 | | 6..10 |
| $pgNum = 3 | 1..5 | T | 1..5 | | 6..10 |
| $pgNum = 4 | 2..6 | | 1..5 | | 6..10 |
| $pgNum = 5 | 3..7 | | 1..5 | | 6..10 |
| $pgNum = 6 | 4..8 | | 1..5 | | 6..10 |
| $pgNum = 7 | 5..9 | | 1..5 | | 6..10 |
| $pgNum = 8 | 6..10 | | 1..5 | T | 6..10 |
| $pgNum = 9 | 7..10 | | 1..5 | T | 6..10 |
| $pgNum = 10 | 8..10 | | 1..5 | T | 6..10 |
+--------------+---------+-------+-------+-------+-------+
Final Result
As a conclusion table.
+--------------+-------+
| VARIABLE |
| $pages_total | 10 |
| $max_links | 5 |
| $lower_limit | 3 |
| $upper_limit | 8 |
+--------------+-------+-------+---------+
| page_number | lower | upper | adjacent|
+--------------+-------+-------+---------+
| $pgNum = 1 | 1..5 | | |
| $pgNum = 2 | 1..5 | | |
| $pgNum = 3 | 1..5 | | |
| $pgNum = 4 | | | 2..6 |
| $pgNum = 5 | | | 3..7 |
| $pgNum = 6 | | | 4..8 |
| $pgNum = 7 | | | 5..9 |
| $pgNum = 8 | | 6..10 | |
| $pgNum = 9 | | 6..10 | |
| $pgNum = 10 | | 6..10 | |
+--------------+-------+-------+---------+
| page_number | if elsif else | result |
+--------------+---------------+---------+
| $pgNum = 1 | | 1..5 |
| $pgNum = 2 | | 1..5 |
| $pgNum = 3 | | 1..5 |
| $pgNum = 4 | | 2..6 |
| $pgNum = 5 | | 3..7 |
| $pgNum = 6 | | 4..8 |
| $pgNum = 7 | | 5..9 |
| $pgNum = 8 | | 6..10 |
| $pgNum = 9 | | 6..10 |
| $pgNum = 10 | | 6..10 |
+--------------+---------------+---------+
7: Summary: Navigation: Adjacent
Now you can enjoy the complete adjacent code as below:
- /_includes/pagination/03-adjacent.html : gitlab.com/…/partials/pagination/03-adjacent.html.
{% capture spaceless %}
{% comment %}
Pagination links
* https://glennmccomb.com/articles/how-to-build-custom-hugo-pagination/
{% endcomment %}
{% if page.paginate_root == nil %}
{% assign paginate_root = "/" %}
{% else %}
{% assign paginate_root = page.paginate_root %}
{% endif %}
{% assign total_pages = paginator.total_pages %}
{% assign page_current = paginator.page %}
{% assign link_offset = 2 %}
{% assign link_max = link_offset | times: 2 | plus: 1 %}
{% assign limit_lower = link_offset | plus: 1 %}
{% assign limit_upper = total_pages | minus: link_offset %}
{% assign min_lower = link_max %}
{% assign max_upper = total_pages | minus: link_max %}
{% assign lower_offset = page_current | minus: link_offset %}
{% assign upper_offset = page_current | plus: link_offset %}
{% endcapture %}
<nav aria-label="Page navigation">
{% if total_pages > 1 %}
<ul class="pagination justify-content-center">
<!-- Page numbers. -->
{% for page in (1..total_pages) %}
{% capture spaceless %}
<!-- Flag Calculation -->
{% assign page_current_flag = false %}
{% if total_pages > link_max %}
<!-- Complex page numbers. -->
<!-- Lower limit pages. -->
<!-- If the user is on a page which is in the lower limit. -->
{% if page_current <= limit_lower %}
<!-- If the current loop page is less than max_links. -->
{% if page <= min_lower %}
{% assign page_current_flag = true %}
{% endif %}
<!-- Upper limit pages. -->
<!-- If the user is on a page which is in the upper limit. -->
{% elsif page_current >= limit_upper %}
<!-- If the current loop page is greater than total pages minus $max_links -->
{% if page > max_upper %}
{% assign page_current_flag = true %}
{% endif %}
<!-- Middle pages. -->
{% else %}
{% if (page >= lower_offset) and (page <= upper_offset) %}
{% assign page_current_flag = true %}
{% endif %}
{% endif %}
{% else %}
<!-- Simple page numbers. -->
{% assign page_current_flag = true %}
{% endif %}
{% endcapture %}
<!-- Show Pager. -->
{% if page_current_flag == true %}
<li class="page-item {% if page == page_current %} active{% endif %}">
{% if page == page_current %}
<span class="page-link">
{{ page }}
</span>
{% elsif page == 1 %}
<a class="page-link"
href="{{ site.baseurl }}{{ paginate_root }}"
>1</a>
{% else %}
<a class="page-link"
href="{{ site.paginate_path | relative_url | replace: ':num', page }}"
>{{ page }}
</a>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</nav>
Notice that this is not the final code, as we want to add some indicator and cosmetic later.
Caveat
That code above will dump a lot of comments.
<!-- Show Pager. -->
Even in capture liquid tag. We also need to remove them later, in final code.
As an alternative, there is also built in Jekyll Liquid comment. But it is hard to read. At least for me.
What is Next ?
Consider continue reading [ Jekyll Pagination - Indicator ].
Thank you for reading.