自动生成文章目录TOC与定位导航

  • 原创
  • Madman
  • /
  • /
  • 0
  • 11103 次阅读

Synopsis: 要自动生成博客文章的目录 TOC(Table of Contents),可以在后端生成,比如 Markdown 有 TOC 插件。或者使用前端插件,比如 tocify、toc 等,同时前端插件还可以结合 scrollspy 等来实现定位导航,即向下滚动文章内容时,会自动定位到对应的目录项。本文使用前端 JS 生成目录,展示样式美观且能够定位导航,并支持 3 级目录

2 bootstrap-toc 预览

1. markdown toc

1.1 markdown.extensions.toc

我的博客使用 Flask 框架,实现文章 Markdown 的模块是 Python-Markdownbleach,所以我只需要启用 markdown.extensions.toc 扩展就能自动从 Markdown 原文中自动生成对应的 TOC

import markdown
...

post = Post.objects.get_or_404(slug=slug)

if post.content_raw:  # content_raw 保存了文章的 Markdown 原文
    md = markdown.Markdown(
        extensions=[
            'markdown.extensions.extra',
            'markdown.extensions.codehilite',
            'markdown.extensions.toc',
        ],
        output_format='html5',
        encoding='utf-8')
    md.convert(post.content_raw)
    toc = md.toc
else:
    toc = None

1.2 {{ toc|safe }}

然后在前端 Jinja2 模板中:

{{ toc|safe }}

即可自动生成文章的目录了:

<div class="toc">
    <ul>
        <li></li>
        <li></li>
        ...
    </ul>
</div>

1.3 CSS 美化

但是现在的文章目录看起来不怎么美观,让我们修改一下,增加相应的 CSS 样式:

<style>
  .u-list-inline {
    padding-left: 0;
    margin-bottom: 0;
    list-style: none;
  }

  .g-ml-15 {
    margin-left: 1.07143rem !important;
  }

  .u-link-v5 {
    text-decoration: none;
    transition: all .2s;
  }

  .u-link-v5:hover, .u-link-v5:focus {
    text-decoration: none;
  }

  /* Color Aqua */
  .g-color-aqua {
    color: #29d6e6;
  }
  .g-color-aqua--hover:hover {
    color: #29d6e6 !important;
  }

  /* Color Red */
  .g-color-red {
    color: #f00 !important;
  }
  .g-color-red--hover:hover {
    color: #f00 !important;
  }
</style>

提供 JavaScript 脚本当文档加载完成后,自动给 .toc 类的目录 div 应用相关的 CSS 样式:

<script>
    $(document).ready(function() {
        // 如果toc为空(只有最外层ul),隐藏整个TOC并返回
        if ($('.toc ul').children().length <= 0) {
            $('.toc').parent().css('display', 'none');
            return;
        }

        // 只显示1、2、3级目录列表
        $('.toc ul li ul li ul li ul').css('display', 'none');
        // 非默认的列表样式
        $('.toc').find('ul').addClass('u-list-inline');
        // 2、3级目录缩进
        $('.toc ul li ul li').addClass('g-ml-15');
        $('.toc ul li ul li ul li').addClass('g-ml-15');
        // 链接颜色,鼠标悬停颜色
        $('.toc').find('a').addClass('u-link-v5 g-color-aqua g-color-red--hover');
    });
</script>

最终的效果图如下:

1 markdown toc 演示

但是它不能跟随页面滚动时自动定位,而且,当你的目录子项很多时,目录将占用很长的纵向位置,不便于目录内容的展示

2. 前端生成 TOC

上面说的后端生成的 TOC 有一些缺陷,所以现在我们在前端通过 JS 来生成 TOC

注意: 请先自行安装 bootstrap

2.1 JS

创建 toc.js 文件,内容如下:

(function($) {
  "use strict";

  window.Toc = {
    helpers: {
      // return all matching elements in the set, or their descendants
      findOrFilter: function($el, selector) {
        var $descendants = $el.find(selector);
        return $el
          .filter(selector)
          .add($descendants)
          .filter(":not([data-toc-skip])");
      },

      generateUniqueIdBase: function(el) {
        var text = $(el).text();

        // Regex for finding the non-safe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ (newlines, tabs, backspace, & vertical tabs)
        var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\\n\t\b\v]/g,
          urlText;

        // Note: we trim hyphens after truncating because truncating can cause dangling hyphens.
        // Example string:                      // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
        urlText = text
          .trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
          .replace(/\'/gi, "") // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
          .replace(nonsafeChars, "-") // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-"
          .replace(/-{2,}/g, "-") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-"
          .substring(0, 64) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-"
          .replace(/^-+|-+$/gm, "") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated"
          .toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated"

        return urlText || el.tagName.toLowerCase();
      },

      generateUniqueId: function(el) {
        var anchorBase = this.generateUniqueIdBase(el);
        for (var i = 0; ; i++) {
          var anchor = anchorBase;
          if (i > 0) {
            // add suffix
            anchor += "-" + i;
          }
          // check if ID already exists
          if (!document.getElementById(anchor)) {
            return anchor;
          }
        }
      },

      generateAnchor: function(el) {
        if (el.id) {
          return el.id;
        } else {
          var 
                                
                            
  • 11206440
  • jerry-han
  • jerrah
  • raniah
  • adhemar
  • skylah
  • martinjr
  • stefannie
  • milasia
  • deajah
  • lamontay
  • shaquna
  • cailynn
  • abhilash
  • eldredge
  • ehaan
  • wrylee
  • suliana
  • jordanne
  • aileigh
  • bronston
  • persephany
  • baudilio
  • frederick
未经允许不得转载: LIFE & SHARE - 王颜公子 » 自动生成文章目录TOC与定位导航

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

0 条评论

暂时还没有评论.

专题系列