有些博客文章很长,像我又不喜欢给文章分页,这样一篇博客读下来鼠标要拖很久,读者比较难定位到关心的内容。解决这个问题有很多措施,一种是在文章拖到底部的时候显示一个返回顶部的按钮(做法参考博客添加回到顶部按钮),另外一种是做一个文章目录,这样就比较直观了。
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代码实现的功能还是比较简单的,以后如果有需要在增加功能把。
ACF搞定一切