Skip to content



Add Tags, Recent Post and new Features to my Blog site

Some must-have features in a blog are Tags and List of recent posts. Tags help to list related posts, and Post List shows recently updated activities. This post decribes a method to add these features based on parsed data.

Last update: 2022-07-01


Tags page#

The tag page is the place to list all tags, and list all pages that have a common tag. A new page will be created at docs\tags\index.md. There is a method to use MkDocs Macros in Markdown template, but it is quite complicated.

Visit the Tags page to see the result.

I use Jinja syntax to create the content of the Tags page, therefore, create a new file at overrides\tags.html and use it as the template for the Tags page:

docs\tags\index.md
---
title: Tags
description: Tags and list of pages
template: tags.html
disqus: ""
hide:
    - navigation
    - toc
---

The tags.html template to include 2 parts:

  • tag-cloud.html: make a tag cloud to see how many pages are associated with a tag
  • tag-list-pages.html: for each tag, list all pages having that tag to show similar articles together
overrides\tags.html
{% extends "main.html" %}

{% block site_nav %}
{% endblock %}

{% block content %}
    <style>
        .md-typeset .tags {
            max-width: 35rem; 
            margin: 0 auto;
        }
        .md-typeset .tags details {
            background-color: aliceblue !important;
            padding: 0.5em 1em;
        }

    </style>
    <div class="tags">
        {% include "partials/tag-cloud.html" %}
        <hr>
        {% include "partials/tag-page-list.html" %}
    </div>
{% endblock %}

Tags will have random colors, to easily disguise them to each other. A helper random_color() macro that returns a random color looks like:

overrides\partials\random-colors.html
{%- macro random_color() -%}
{{- ["DarkRed",
"DarkGoldenrod",
"DarkGreen",
"DarkOliveGreen",
"DarkCyan",
"DarkTurquoise",
"DarkBlue",
"DarkMagenta",
"DarkViolet",
"DarkSlateBlue",
"DarkOrchid",
"DarkSlateGray"] | random -}}
{%- endmacro -%}

Then it can be imported and used:

{% from "partials/random-colors.html" import random_color %}

<span style="color:{{ random_color() }};">tag</span>

Tag cloud#

The tag cloud shows all tags in different size and color. The bigger a tag is, the more pages mention that tag. Steps to make a tag cloud:

  1. Scan all pages and create a list of pairs (tag, pages[]).

  2. Count the number of pages for each tag then show each tag with different text size and color using font-size and color attributes.

overrides\partials\tag-cloud.html
{% from "partials/colors.html" import color %}

{% set tags=[] %}

{# scan all pages #}
{% for p in pages %}
  {% if p.page.meta.tags %}
    {# extract tags if available #}
    {% for tag in p.page.meta.tags %}
      {% if tags|length %}
        {% set ns = namespace(found=False) %}
        {# read more about scope at 
        https://jinja.palletsprojects.com/en/2.11.x/templates/#assignments
        #}
        {# check if tag exists, append to its page list #}
        {% for item in tags %}
          {% set t, ps = item %}
          {% if tag == t %}
            {% set ns.found = True %}
            {# use "" to not add spaces in content #}
            {{ ps.append(p.page) or "" }}
          {% endif %}
        {% endfor %}
        {# if tag doesn't exist, create new page list#}
        {% if not ns.found %}
          {{ tags.append((tag, [p.page])) or "" }}
        {% endif %}
      {% else %}
        {{ tags.append((tag, [p.page])) or "" }}
      {% endif %}
    {% endfor %}
  {% endif %}
{% endfor %}

<style>
    .tag-cloud {
        margin-top:0;
        margin-bottom: 0.5em;
    }
    .tag-cloud-content {
        padding: 0 0.6rem;
        {% if page.url == 'tags/' %}
        text-align: center;
        {% endif %}
    }
</style>

<p class="md-nav tag-cloud">
  <label class="md-nav__title">Tag cloud</label>
</p>
<div class="tag-cloud-content">
    {% if tags|count %}
        {% for item in tags %}
            {% set tag, ps = item %}
            {# create a link with tag name #}
            {# font size is based on the page count #}
            <a class="tag" href="{{ config.site_url }}tags/#{{ tag }}">
                <span class="tag-name" style="
                    {% set sz = ps|count %}
                        {% if sz > 10 %}
                            {% set sz = 10 %}
                        {% endif %}
                    {% if page.url == 'tags/' %}
                        font-size:{{ 1+sz*0.05}}rem;
                    {% else %}
                        font-size:{{ 0.5+sz*0.05}}rem;
                    {% endif %}
                    color:{{ color( loop.index%12) }};
                ">
                    {{- tag -}} &nbsp;
                </span>
                <!--<sup class="tag-count">{{- ps|count -}}</sup>-->
            </a>
        {% endfor %}
    {% else %}
        <p>
            No tag found!
        </p>
    {% endif %}
</div>

Page list#

This section is simple as it just needs to loop through the list of pairs (tag, pages[]) and create a link to each page. Steps to take that:

  1. Scan all pages and create a list of pairs (tag, pages[]).

  2. Show each tag with the list of pages in a collapsible <details> block.

  3. Only one tag block is open at a time to easily follow the selected tag. To do this, I added a callback of the toggle event on all tag blocks. Whenever a block is opened, this script will close all others.

  4. A tag block can be opened via URL with hash being the selected tag.

overrides\partials\tag-page-list.html
{% set tags=[] %}

{# scan all pages #}
{% for p in pages %}
  {% set pg = p.page %}
  {% set hidden = true if (pg.meta and pg.meta.hide and ('in_recent_list' in pg.meta.hide)) %}
  {% if pg.meta.tags and not hidden %}
    {# extract tags if available #}
    {% for tag in pg.meta.tags %}
      {% if tags|length %}
        {% set ns = namespace(found=False) %} 
        {# read more about scope at
        https://jinja.palletsprojects.com/en/2.11.x/templates/#assignments 
        #}
        {# check if tag exists, append to its page list #}
        {% for item in tags %}
          {% set t, ps = item %}
          {% if tag == t %}
            {% set ns.found = True %}
            {# use "" to not add spaces in content #}
            {{ ps.append(pg) or "" }}
          {% endif %}
        {% endfor %}
        {# if tag doesn't exist, create new page list#}
        {% if not ns.found %}
          {{ tags.append((tag, [pg])) or "" }}
        {% endif %}
      {% else %}
        {{ tags.append((tag, [pg])) or "" }}
      {% endif %}
    {% endfor %}
  {% endif %}
{% endfor %}

<style>
    .md-typeset .tag summary::before {
        height: 1rem;
        width: 1rem;
        margin-top: 0.25em;
    }
</style>
<div class="tag-page-list">
  {% for item in tags %}
    {% set tag, ps = item %}
    <details class="tag" id={{ tag }}>
      <summary>
        {{- tag }} ({{- ps|count -}})
        <a class="headerlink" href="#{{ tag }}">⚓︎</a>
      </summary>
      <ol>
        {% for p in ps %}
        <li>
          <a href="{{ p.canonical_url }}">
            {%- if p.meta and p.meta.title -%}
              {{- p.meta.title -}}
            {%- else -%}
              {{- p.title -}}
            {%- endif -%}
          </a>
        </li>
        {% endfor %}
      </ol>
    </details>
  {% endfor %}
</div>

<!-- expand page list for only selected tag -->
<script>
  [...document.getElementsByTagName("details")].forEach((D, _, A) => {
    D.open = false
    D.addEventListener("toggle", E =>
      D.open && A.forEach(d =>
        d != E.target && (d.open = false)
      )
    )
  }
  )

  var hash = window.location.hash.substr(1);
  if (hash) {
    document.getElementById(hash).open = true;
  }
</script>

Main template#

The main.html file, extending the base.html template, will be used for all markdown pages, and it is the starting point to add custom template.

To override it, add the main.html file in the overrides folder. Here are things I’m going to do to add more content into a blog post:

  1. Extract metadata to get title, description, tags, and other information.

  2. Add block to use the Open Graph protocol to show the page’s information when a user shares a page on a social network.

  3. Include modified Navigation section to show Tag cloud in either left or right panel.

  4. Include modified Page Content which renders the content with additional sections (cover, table of content, main content, comments.).

overrides\main.html
{# page info #}
{% set page_title = '' %}
{% if page and page.meta and page.meta.title %}
    {% set page_title = page.meta.title %}
{% elif page and page.title and not page.is_homepage %}
    {% set page_title = page.title %}
{% endif %}

{% if page.markdown == '' and page.parent.children %}
    {% if page and page.meta and page.meta.title %}
        {% set page_title = page.meta.title %}
    {% else %}
        {% set page_title = page.parent.title  %}
    {% endif %}
{% endif %}

{% set page_description = '' %}
{% if page and page.meta and page.meta.description %}
    {% set page_description = page.meta.description %}
{% elif page and page.description and not page.is_homepage %}
    {% set page_description = page.description %}
{% endif %}

{% set page_url = page.canonical_url %}

{% set page_image = config.site_url ~ "assets/banner.jpg" %}
{% if page and page.meta and page.meta.banner %}
    {% set page_image = page.canonical_url ~ page.meta.banner %}
{% endif %}

{% if page and page.meta and page.meta.tags %}
    {% set page_tags = page.meta.tags %}
{% endif %}

{# template #}
{% extends "base.html" %}

{# title #}
{% block htmltitle %}
    <title>{{ page_title }} - {{ config.site_name }}</title>
{% endblock %}

{# sharing #}
{% block extrahead %}
    {% include "partials/ads.html" %}
    <!-- Open Graph -->
    <meta property="og:type" content="website" />
    <meta property="og:title" content="{{ page_title }} - {{ config.site_name }}" />
    <meta property="og:description" content="{{ page_description }} - {{ config.site_description }}" />
    <meta property="og:url" content="{{ page_url }}" />
    <meta property="og:image" content="{{ page_image }}" />
    <meta property="og:image:type" content="image/png" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />

    <!-- Twitter Cards -->
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="{{ page_title }} - {{ config.site_name }}" />
    <meta name="twitter:description" content="{{ page_description }} - {{ config.site_description }}" />
    <meta name="twitter:image" content="{{ page_image }}" />
{% endblock %}

{# navigation #}
{% block site_nav %}
    {% include "partials/navigation.html" %}
{% endblock %}

{# content #}
{% block content %}
    {% include "partials/post-content.html" %}
{% endblock %}

The sidebar will display the tag cloud based in the page’s table of content.

overrides\partials\navigation.html
{% if nav %}
    {% if page.meta and page.meta.hide %}
        {% set hidden = "hidden" if "navigation" in page.meta.hide %}
    {% endif %}

    <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" {{ hidden }}>
        <div class="md-sidebar__scrollwrap">
            <div class="md-sidebar__inner">
                {% include "partials/nav.html" %}

                {# show tags on the left side if the right side has toc #}
                {% if page.toc %}
                    <br>
                    <br>
                    <div class="tag-cloud-nav">
                        {% include "partials/tag-cloud.html" %}
                    </div>
                {% endif %}
                <br/>
                {% include "partials/ads_sidebar.html" %}
            </div>
        </div>
    </div>
{% endif %}

{% if not "toc.integrate" in features %}
    {% if page.meta and page.meta.hide %}
        {% set hidden = "hidden" if "toc" in page.meta.hide %}
    {% endif %}

    <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" {{ hidden }}>
        <div class="md-sidebar__scrollwrap">
            <div class="md-sidebar__inner">
                {% if not page.is_homepage %}
                    {% include "partials/toc.html" %}
                {% endif %}

                {# show tags on the right side if there is no toc there #}
                {% if page.is_homepage or not page.toc %}
                    <div class="tag-cloud-toc">
                        {% include "partials/tag-cloud.html" %}
                    </div>
                {% endif %}
                <br/>
                {% include "partials/ads_sidebar.html" %}
            </div>
        </div>
    </div>
{% endif %}

Page content#

The page content will be placed in the main block. If there is no content, a list of children posts will be shown.

overrides\partials\post-content.html
{# edit button #}
{% if page.edit_url %}
    <a href="{{ page.edit_url }}" title="{{ lang.t('edit.link.title') }}" class="md-content__button md-icon">
        {% include ".icons/material/pencil.svg" %}
    </a>
{% endif %}

{% include "partials/post-cover.html" %}

<hr class="screen-only">

{% include "partials/post-toc.html" %}

{# show the children pages if no content #}
{% if page.markdown == '' and page.parent.children %}
    <h2>Posts in this section:</h2>
    <ol>
        {% for obj in page.parent.children %}
            {% if obj.is_section %}
                {% set p = obj.children[0] %}
                <li>
                    <a href="{{ p.canonical_url }}">
                        {%- if p.meta and p.meta.title -%}
                            {{- p.meta.title -}}
                        {%- else -%}
                            {{- p.title -}}
                        {%- endif -%}
                    </a>
                </li>
            {% endif %}
        {% endfor %}
    </ol>
{% else %}
    {# content #}
    {{ page.content }}
{% endif %}

{% if page.markdown == '' and page.parent.children %}
{% else %}
    {# comment #}
    {% include "partials/disqus.html" %}
{% endif %}

When printing to a PDF file, the first page should show the post title and its short description. This page is called the cover page which will be created only in printing mode.

Create an element with class cover in the post-cover.html template to wrap the cover section. In print mode, this element should cover the full height (100%) of the first paper and align its content vertically. After the line of tags, the updated date will be shown to easily check the latest version of the document:

overrides\partials\post-cover.html
{# the cover page #}
<style>
    .md-typeset .cover {
        margin-bottom: 1em;
    }
    .md-typeset .page-category {
        color: gray;
        font-size: large;
    }
    .md-typeset .page-title {
        margin-left: -0.0625em;
    }
    .md-typeset .page-extra {
        color: gray;
        font-size: small;
    }
    .md-typeset .page-tags {
        margin: 0;
    }
    .md-typeset .page-date {
        margin: 0;
        text-align: end;
    }
    @media print {
        .md-typeset .cover {
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
        }
        .md-typeset .cover + * {
            margin-top: 0;
        }
    }
</style>

<div class="cover">
    {# category #}
    {% if page.meta and page.meta.category %}
    <span class="page-category">
        {{ page.meta.category }} »
    </span>
    <br>
    {% endif %}

    {# title #}
    <h1 class="page-title"> {{ page_title | d(config.site_name, true) }} </h1>

    {# description #}
    {% if page.meta and page.meta.description %}
        <p class="page-description">
            {{ page.meta.description }}
        </p>
    {% endif %}

    {% if page.markdown == '' and page.parent.children %}

    {% else %}
        <div class="page-extra row">
            <div class="col">
            {% if page.meta and page.meta.tags %}
                <p class="page-tags">
                    {% for tag in page.meta.tags %}
                        <a class="tag" href="{{ config.site_url }}tags/#{{tag}}">
                            <span class="tag-name">
                                #{{ tag }} &nbsp;
                            </span>
                        </a>
                    {% endfor %}
                </p>
            {% endif %}

            </div>
            <div class="col">
                <p class="page-date">
                    <span>
                    {% if page.meta.git_revision_date_localized_raw_iso_date %}
                        {{ lang.t("source.file.date.updated") }}:
                        {{ page.meta.git_revision_date_localized_raw_iso_date }}
                    {% endif %}
                    </span>
                </p>
            </div>
        </div>
    {% endif %}
</div>

When displaying on a screen, the Table of Content is displayed in the right sidebar. In printed pages, there should be a page to display the table of content too. This page is also only visible in printing.

The base Material for MkDocs theme has a partial block for Table of Content section, so I just need to declare it in post-toc.html and include it in the main.html template, between the cover page and the main content.

overrides\partials\post-toc.html
{# the table of content page #}
<style>
    .md-typeset .toc {
        display: none;
    }
    .md-typeset .toc label {
        display: none;
    }
    .md-typeset .toc .md-nav {
        font-size: unset;
        line-height: 1.6;
    }
    .md-typeset .toc .md-nav--secondary {
        margin-left: -2em;
    }
    .md-typeset .toc .md-nav__list {
        margin: 0;
    }
    .md-typeset .toc ul {
        list-style: none;
    }
    @media print {
        .md-typeset .toc {
            display: block;
            page-break-after: always;
        }
        .md-typeset .toc .md-nav__link {
            color: var(--md-typeset-a-color);
        }
        .md-typeset .toc .md-nav__link.md-nav__link--active {
            font-weight: unset;
        }
        .md-typeset .toc + * {
            margin-top: 0;
        }
    }
</style>

<div class="toc">
    <h2>Table of Content</h2>
    {% include "partials/toc.html" %}
</div>

Jinja object

It is easy to display an object in Jinja template as Jinja is based on Python.

To show all attributes:

{{ page.__dict__}}

To show a specific attribute:

{{ page.parent.children }}

The recent blog posts#

There should be a page showing the recent posts to help users see what is new and updated. With the Revision Date plugin, it is able to use two new meta-data fields: git_revision_date_localized, and git_creation_date_localized if the option enable_creation_date is true.

Create new index.md file inside the blog folder. When using the Section Index plugin, this index file will be merged to the Blog section, therefore, when user selects the Blog label, there is a list of recent posts will be shown.

docs\blog\index.md
---
title: Recent posts
description: The lastest activities show in the list of recently updated post. Please read the post title and description and choose any post which seems interesting to you. I hope you always can find something new here.
template: blog.html
disqus: ""
---

This page will use the blog.html template in which it scans all posts and check the creation date to make a list of posts. Each post should be displayed in a container and be formatted to show the title, the description (at most 250 character using the truncate filter), the creation date, and its tags.

Here is the code to sort all pages in order of creation date, and then filter all blog posts to save into the array blog_pages which will be used to generate content.

{% set blog_pages=[] %}

{% for p in pages|sort(     
    attribute='page.meta.git_revision_date_localized',
    reverse=True
    ) 
%}

    {% set pg = p.page %}

    {# do not list homepage, empty pages, hidden pages #}
    {% set hidden = true if (pg.meta and pg.meta.hide and ('in_recent_list' in pg.meta.hide)) %}
    {% if (not pg.is_homepage) and 
            (not pg.markdown == '') and 
                (not hidden)
    %}
        {{ blog_pages.append(pg) or "" }} {# use "" to not add spaces in content #}
    {% endif %}
{% endfor %}

Groups of pages#

When the number of posts goes bigger, the recent post list becomes longer. It’s time to brake the long list into pages — the user can click on the page number to see its children posts.

This is called “Pagination”. How to implement it?

Jinja template has the slice filter to divide a list into sub-lists. Here, I’d like to have maximum of 10 posts on each page.

{# count the number of pages #}
{% set page_num = (blog_pages|count / 10)|round(method='ceil')|int %}

<div id="page_num" data-value="{{page_num}}"></div>

<div class="pages">
    {% for pg_group in blog_pages|slice(page_num) %}
    <div class="page" id="page{{ loop.index }}">
        {% for pg in pg_group %}
            <div class="post">
                ... create post layout and content ...
            </div>
        {% endfor %}
    </div>
{% endfor %}
</div>

Post-entry#

Each post is wrapped inside a <div class="post"> and its elements are marked with different classes, such as post-title, post-description, etc. for applying styles later.

<div class="post">
    <h4 class="post-title">
        <a href="{{ pg.canonical_url }}">{{ pg.title }}</a>
    </h4>
    <div class="post-info">
        <div>
            <p class="post-description">
                {% if pg.meta and pg.meta.description %}
                {{ pg.meta.description | truncate(200) }}
                {% endif %}
            </p>
            <div class="post-extra row">
                <div class="col">
                    {% if pg.meta and pg.meta.git_revision_date_localized %}
                    <p class="post-date">
                        <span>
                            {{ pg.meta.git_revision_date_localized }}
                        </span>
                    </p>
                    {% endif %}
                </div>
                <div class="col">
                    {% if pg.meta and pg.meta.tags %}
                    <p class="post-tags">
                        {% for tag in pg.meta.tags %}
                        <a class="tag" href="{{ config.site_url }}tags/#{{tag}}">
                            <span class="tag-name" style="color:{{ random_color() }};">
                                #{{ tag }}
                            </span>
                        </a>
                        {% endfor %}
                    </p>
                    {% endif %}
                </div>
            </div>
        </div>
        {% if pg_image %}
        <img class="post-banner "src='{{ pg_image }}'/>
        {% endif %}
    </div>
</div>

Here is a simple style to make each post display necessary basic information:

.md-typeset .post {
    margin-bottom: 1rem;
}
.md-typeset .post .post-title {
    margin: 0.25rem 0;
    text-decoration: none;
    font-size: 1.3em;
}
.md-typeset .post .post-info {
    display: flex;
}
.md-typeset .post .post-banner {
    margin-top: -1rem;
    max-height: 6rem;
    border: 1px solid lightgray;
}
.md-typeset .post .post-description {
    margin: 0 1rem 0 0;
}
.md-typeset .post .post-extra {
    margin: 0.5rem 1rem 0 0;

    color: darkgray;
}
.md-typeset .post .post-tags {
    margin: 0;
    text-align: end;
}
.md-typeset .post .post-date {
    margin: 0;
}

Pagination bar#

To show the current active page, I use pure CSS and JavaScript. The idea is to use the URL hash to detect which page is activated, such as #page1.

{# pagination #}
<div class="pages">
    {% for pg_group in blog_pages|slice(page_num) %}
        <div class="page" id="page{{ loop.index }}">
            {% for pg in pg_group %}
                {% set pg_image = "" %}
                {% if pg.meta and pg.meta.banner %}
                    {% set pg_image = pg.canonical_url ~ pg.meta.banner %}
                {% endif %}
                <div class="post">
                    ...
                </div>
            {% endfor %}
        </div>
    {% endfor %}
</div>

<hr>

<div class="center">
    <div class="pagination" id="pagination-bottom">
        <!-- <a href="#">&laquo;</a> -->
        {% for pg_group in blog_pages|slice(page_num) %}
        <a class="page-number {% if loop.index==1 %}active{% endif%}" href="#page{{ loop.index }}">{{ loop.index }}</a>
        {% endfor %}
        <!-- <a href="#">&raquo;</a> -->
    </div>
</div>

<hr>

<p class="center">Total <b>{{ blog_pages|count }}</b> posts in {{ page_num }} pages.</p>

Then add some styles to the pagination block and its children links:


CSS Styles:

Use target keyword to select the selected page ID, then show only the target element.

.md-typeset .pages > .page:target ~ .page:last-child,
.md-typeset .pages > .page {
    display: none;
}
.md-typeset .pages > :last-child,
.md-typeset .pages > .page:target {
    display: block;
}

JavaScript

When the page is loaded, a script will run to get all pagination’s links, and then add a callback function for click event, that remove active class from last activated element and then assign active class to the event’s source element. Note that the first page is activated by default when the page is loaded. After a page is selected, function scrollToTop() will navigate to the top view.

overrides\partials\post-list.html
<script>
    function scrollToTop() {
        // delay a little for css to calculate windows size
        setTimeout(function () {
            window.scrollTo(0, 0);
        }, 100);
    }

    function activatePaginationLinks(name) {
        var pagination = document.getElementById("pagination-"+name);
        if (pagination) {
            var links = pagination.getElementsByClassName("page-number");
            if (links.length) {
                for (var i = 0; i < links.length; i++) {
                    if (links[i].getAttribute("href") == window.location.hash) {
                        links[i].classList.add("active");
                    } else {
                        links[i].classList.remove("active")
                    }
                }
            }
        }
    }

    // show page 1 as default
    window.location.hash = "#page1";

    // listen to hash change
    window.onhashchange = function() {
        var hash = window.location.hash;
        const regexp = /^#page[0-9]+$/;
        if (regexp.test(hash)) {
            var num = parseInt(hash.substr(5));
            var max = parseInt(document.getElementById('page_num').dataset.value);
            if(num >= 1 && num <= max) {
                activatePaginationLinks("top");
                activatePaginationLinks("bottom");
                scrollToTop();
                return;
            }
        } 

        window.location.hash = "#page1";
    }
</script>

Zoom-in Images#

As mentioned in the Images section, view-bigimg library helps to zoom and pan images. It’s useful when the image is in high resolution and resized to fit site’s width.

Download view-bigimg.css and view-bigimg.js files from the view-bigimg repo, then add them into the addition assets configs in mkdocs.yml:

extra_css:
    - assets/view-bigimg.css
extra_javascript:
    - assets/view-bigimg.js

When click on the image, this library will create a new layer and show the image in a bigger size. However, it must be clicked on the close button to go back to the page’s content. I want to simplify this step by just click on the image. Panning still is activated by press and hold. Therefore, I write a function to detect mousedown and mousemove event, then only close the image if it is a simple click:

assets\extra.js
var dragged = false;
document.addEventListener("mousedown", () => (dragged = false));
document.addEventListener("mousemove", () => (dragged = true));

var viewer = new ViewBigimg();
var figures = document.querySelectorAll("img");
for (var i = 0; i < figures.length; i++) {
    figures[i].onclick = (e) => {
        if (e.target.nodeName === "IMG") {
            viewer.show(e.target.src);
        }
    };
}
var containers = document.querySelectorAll("#iv-container .iv-image-view");
for (var i = 0; i < containers.length; i++) {
    containers[i].onclick = () => {
        if (!dragged) {
            viewer.hide();
        }
    };
}

When following links, to remain the blog page opened, external links should be shown in new tabs without any tracking information. To do that, I write some lines of code to get all external links in the page, then set target = "_blank" and add attribute rel = "noopener noreferrer" to them.

assets\extra.js
/* open external links in new tab */
var links = document.links;
for (var i = 0, linksLength = links.length; i < linksLength; i++) {
    if (links[i].hostname != window.location.hostname) {
        links[i].target = "_blank";
        links[i].setAttribute("rel", "noopener noreferrer");
        links[i].className += " externalLink";
    } else {
        links[i].className += " localLink";
    }
}

Comments