有些博客文章很长,像我又不喜欢给文章分页,这样一篇博客读下来鼠标要拖很久,读者比较难定位到关心的内容。解决这个问题有很多措施,一种是在文章拖到底部的时候显示一个返回顶部的按钮(做法参考博客添加回到顶部按钮),另外一种是做一个文章目录,这样就比较直观了。

TOC+插件

我之前用Table of Contents Plus插件来实现这个功能,这个插件设置还很多,我之所以选择它,是因为这个插件提供一个文章目录小工具,可以放到侧栏。其它同类的插件或者实现,都是把目录显示在文章内部,比如开头或者结尾,但是在阅读过程中,这其实还是跟没有一样。

我个人认为,最能体现文章目录优点的方法是:文章目录显示在侧栏,并且跟随阅读过程。后者已经有人实现了,我的博客也使用了这段代码,可以参考博客侧边栏跟随滚动效果这一篇文章。那现在的问题就是,如何把文章目录小工具显示在侧栏制定的位置(如果要有跟随的脚本,前提是把要跟随的内容放在指定的容器内)。TOC+插件有提供小工具的显示代码,但是点击侧栏文章目录的链接无法回到相应的标题,看了下是锚点的链接不一致。

JavaScript实现

既然如此,就自己实现一个,原理很简单,从文章中找出<h3>标签,并且给它添加一个id="#i_x'(作为锚点, x为标题的序号),然后把标题内容追加到侧栏制定的位置,生成一个列表,列表的每一项是链接,链接的地址为'#i_x'。

效果如下图所示:

侧栏显示文章目录

实现代码

代码很简单,如下所示:

/* Display table of contents */
function show_toc(toc_selector, wrap_id, min_nr)
{
    var wrap = document.getElementById(wrap_id);

    var hlist = document.querySelectorAll(toc_selector);

    if (!wrap)
        return;

    if (!hlist || hlist.length <= min_nr) {
        wrap.style.display = 'none';
        return;
    }

    var ul = document.createElement('ul'), li, link;
    
    for (i = 0; i < hlist.length; i++) {
        hlist[i].id = 'i_' + i;

        li = document.createElement('li');

        link = document.createElement('a');
        link.href = '#' + hlist[i].id;
        link.className = 'toc_link';
        link.innerHTML = hlist[i].innerHTML;
        
        li.appendChild(link);
        ul.appendChild(li);
    }

    wrap.appendChild(ul);
}

其中:

  • toc_selector为标题的选择符,例如'#content h3',找出文章中所有<h3>标签;
  • wrap_id为侧栏容器的id,表示你要把目录放到哪个位置;
  • min_nr是显示目录的最小标题个数,如果不满足则不在侧栏显示;

使用方法

在sidebar.php添加一下代码(假设你已经安装了侧栏跟随的脚本):

<div id='sidebar-follow'>

<div class="section" id="show-toc">
<h2>文章目录</h2>
</div>
<div>

在这之后,或者在footer位置添加以下代码:

<script type="text/javascript">
show_toc('#content h3', 'show-toc', 3);
</script>

更新:仅在post或者page页面才显示:

<?php if (is_singular()): ?>
<div class="section" id="show-toc">
<h2>文章目录</h2>
</div>
<?php endif?>

OK,大功告成。

改进:平滑滚动

在此基础上,还可以让文章标题跳转时做到平滑滚动。这需要用到jQuery,不过一般的博客都会事先加载jQuery库。

在任意位置添加一下JavaScript代码即可:

<script type="text/javascript">
    /* 在锚点链接间平滑滚动 */
    $(document).on('click', 'a[href*=#]', function(e) {           
        if ((location.pathname.replace(/^\//, '') != this.pathname.replace(/^\//, ''))
            || (location.hostname != this.hostname))
            return true;

        var $target = $(this.hash);
        $target = $target.length && $target || $('[name=' + this.hash.slice(1) + ']');

        if ($target.length) {
            var targetOffset = $target.offset().top;

            $('html,body').animate({scrollTop: targetOffset}, 1000);
            return false;
        }
    });
</script>

我自己不大喜欢在页面加太多script标签,所以我博客的JavaScript代码都是合并到一个文件的,我的JavaScript文件在这里

后记

我这里通过JavaScript代码实现的功能还是比较简单的,以后如果有需要在增加功能把。