在浏览一些博客或 wiki 类网站的时候,你可能会发现在文章的开头或侧边会有一个目录,点击就可以跳转到指定的章节。对于比较长的文章来说,目录还是比较重要的。通过目录可以快速了解文章包含的内容,要查看某个章节也可以直接跳转。

最近准备给博客增加一个生成文章目录的功能,下面简单写一下实现方式。

代码

我这里使用了 jQuery,代码如下:

//  给 h2 到 h5 增加一个 content-title 的 class
$('.post-content h2').addClass('content-title');
$('.post-content h3').addClass('content-title');
$('.post-content h4').addClass('content-title');
$('.post-content h5').addClass('content-title');
//  给 h2 到 h5 增加一个 data-index 的自定义属性
$('.post-content h2').attr('data-index', 2);
$('.post-content h3').attr('data-index', 3);
$('.post-content h4').attr('data-index', 4);
$('.post-content h5').attr('data-index', 5);

//  函数的一个参数是标题级别,第二个参数是第一个标题的索引值
function atalog(titleIndex, start) {
    //  存储 HTML 和当前的索引值
    var el = {
        el: '',
        index: start
    };
    var current = 0;  //  已遍历的数量

    for (var i = start;i < $('.content-title').length;i ++) {
        if (i < current) {
            //  如果当前 i 的值小于已遍历的数量就跳过
            continue;
        }

        if ($('.content-title').eq(i).attr('data-index') > titleIndex) {
            //  如果是更小一级的标题就调用自身继续查找
            var result = atalog($('.content-title').eq(i).attr('data-index'), i);
            //  把返回的 HTML 添加到当前函数的 el 中
            el.el += '<li> ' + result.el + '</li>';
            current = result.index + 1;  //  设置已遍历的数量
            el.index = result.index;  //  设置索引
            continue;  //  跳过本次循环
        }

        if ($('.content-title').eq(i).attr('data-index') < titleIndex) {
            //  如果是更大一级的标题就返回已生成的 HTML 目录
            el.el = '<ul class="mb-2">' + el.el + '</ul>';
            return el;
        }
        //  生成 HTML 目录
        el.el += '<li><a data-index="' + i + '" href="javascript:;">' + $('.content-title').eq(i).text() + '</a></li>';
        el.index = i;  //  设置当前的索引值为 i
    }
    //  添加列表的外层 ul
    el.el = '<ul class="mb-2"> ' + el.el + '</ul>';
    return el;  //  返回生成的 HTML 目录
}

//  调用生成目录的函数,从第 0 个 h2 开始
var el = atalog(2, 0);
//  把生成的目录插入到文章的开头
$('.post-content').prepend('<div class="atalog">' + el.el + '</div>');
//  给生成的目录添加点击事件
$('.post-content .atalog a').on('click', function () {
    //  设置滚动条的高度为标题的 offsetTop
    $(document).scrollTop($('.content-title').eq($(this).attr('data-index')).offset().top);
});

上面的文章内容就在 classpost-content 的元素中。

简单说明

上面生成目录是从 h2 开始遍历,不包含 h1

先给 h2h3h4h5 添加相同的 class,再给 h2h3h4h5 添加 2345data-index 属性,用来区分标题等级。

函数的第一个参数是最大的标题等级,上面设置的是 2 ,也就是代表 h2。第二个参数是开始遍历的索引,上面设置为 0 就是从第 0 个标题开始遍历。

如果遍历到更小一级的标题就调用自身,传入更小一级的标题和当前的索引值继续遍历。如果遍历到更大一级的标题就返回生成的 HTML 目录。

遍历完成后生成 HTML 目录列表,然后返回 HTML 目录列表。

目录跳转可以通过改变滚动条的高度来跳转,也可以给标题和链接添加 id 跳转。如果通过 id 跳转的话,一些使用固定定位的导航栏可能会挡住标题,而通过改变滚动条高度来跳转就可以很方便的设置标题显示的位置。

效果

Sass 简单使用教程 这篇文章为例,生成的目录如下:

js生成的文章目录

上面图片中的目录还没有添加 CSS。

通过 JS 生成文章目录的方式有很多,我这里的算是比较简单的一种,但不是最优的一种。