其实上篇已经基本实现了目录的功能,优化一下内部样式就可以完事了。但是作为一个完美主义者,距离我想要的下滑自动跟随页面,自动定位文章在目录中的位置这样的功能还是相差甚远。
TOC Plugin For Bootstrap
在网上寻找解决对应的轮子时,找到一篇 文章 介绍了 Bootstrap 中的 Scrollspy 组件,震惊到原来其实 Bootstrap 已经提供了完美的解决方案,看来还是对 Bootstrap 不了解。之后还是要补一补。
而在该文章的第一个评论者说自己实现了给 Bootstrap 用的 TOC 插件。传送门 可以直接使用他做的插件来实现这个导航栏的功能。(需要使用 BootStrap 的 js 和 css 样式。)
根据他的教程,引入 Bootsrap 和TOC插件,成功实现了对应的锚点、定位等功能。主要是给 body
添加 data-spy="scroll" data-target="#toc"
这一串属性,然后添加一个toc
的nav
框架,添加上data-toggle="toc" data-spy="affix" class="affix">
这一串必要的标识。
1 | <body data-spy="scroll" data-target="#toc"> |
基于这个插件最终成功实现了锚点、定位等功能,但是目前存在诸多优化的问题。需要一个个解决。
TOC样式逻辑优化
经过测试发现,控制 TOC 样式的文件是 bootstrap-toc.min.css
而不是bootsrap.min.css
,因此直接删除该文件,主要研究bootstrap-toc.min.css
的样式特征。
作为一个主题来说,颜色应该做到统一,因此需要调用主题色,而单纯的 CSS 文件没法进行这样的调用,因此要使用 SASS 的方式进行设计。
重命名bootstrap-toc.min.css
为toc.scss
,并用 Chrome 格式化成可阅读模式,放入dev
文件夹中。在app.scss
中加入下列代码,使得 SASS 在生成 css 时也会把 toc.scss
这个文件加载进去。
1 | @import 'toc'; |
用$color-theme-default
来替换所有的紫色色值。完成效果如下
目录布局调整
从修改逻辑上来说,应该先进行白天模式的修改,在此基础上再调整夜间模式的效果。因此将关掉夜间模式,对白天模式进行样式的调整。
目前的框架结构是这样的
1 | <main class="post-container home-content"> |
我希望将目录移动到右边,那么作为基本容器的post-toc-wrapper
和post-article
的 position
必须要是 relative
,同时让post-toc-wrapper
的float
值为right
,如此一来,目录位置就会挪到右边。
接下来则是对固定跟随功能的设计。我希望达到的效果是:目录在页面刚滚动时随页面一起滚动,但到达某一位置后,它开始固定在页面上;页面滚动到评论部分时,目录不再固定,又随页面一起滚动。
这一个功能 其实Bootsrap 的 Affax 插件已经完全实现了。
从字面上翻译,则 affix 是固定的意义。这个插件里,这固定是有条件的。先来看下 affix 的效果,就是本篇右侧的目录导航。
在这边说一下应用的过程。
Affix 插件的使用
页面滚动过程中,Affix 插件会根据我们的配置参数切换应用到目录部分的 CSS 类,整个滚动过程会产生三个类:affix-top
、affix
、affix-bottom
。
插件提供的配置参数 offset: { }
,里面包括两个值:top 和 bottom。
整个过程用文字描述如下:
- 页面加载完毕后,应用 affix 效果的内容会增加一个
affix-top
样式类 - 当页面向下滚动了 top 的距离时,
affix-top
切换成affix
类 - 页面滚动到离底部距离为 bottom 时,
affix
类切换成affix-bottom
。
这样,我们根据需要定义这三个类的样式就好了。
因为前边已经引入了 JQuery 的库,这里就直接加入下列代码即可。
1 | $('#toc').affix({ |
这里我通过 JavaScript 设置 offset 值,而不是直接在 HTML 标签中应用属性 data-spy="affix"
、data-offset-top
与 data-offset-bottom
,这是因为顶部和评论部分的高度无法确定,top 和 bottom 的值只能动态计算。
这样,页面加载完成后,#toc 有一个 affix-top
类,在滚动 top 值后,# toc 部分有一个 affix
类,在离页面底部距离 bottom 值时,#toc 部分的类又变成 affix-bottom
。
以下是 CSS 样式。
1 | .affix{ |
插件会自动计算 affix-bottom
和affix-top
类的 top 值,所以无需多余的设置。
目录样式
position 为 fixed 时需要用 margin 来调整尺寸大小。
因为内部 a
自带 padding属性,所以去掉左右 padding 的值,只保留上下的。
1 | .post-toc-wrapper { |
最后把字体大小调整为15px。
经过调整后的形式。
夜间模式同样效果良好
TOC 内容调整
因为默认生成的 TOC 带有标题,有标题的 TOC 实在看着奇怪,因此要把这个部分去掉。通过加入data-toc-skip
即可实现。
1 |
|
TOC宽度自适应
横排菜单
使用给 li 元素附加上 float:left
参数后就能实现目录栏的横排,但是这个时候显示并不对。
因为是 display:block
的关系,所以post-toc
的样式不会随内部元素的变化而变化。因此将其修改为display:flex
,便能自动适应内部元素的尺寸。自适应良好。
1 | @media screen and (max-width: $post-container-w) { |
下拉式二级目录
虽然说目前实现了所有的功能,但是次级目录的显示方式还是十分变扭。
之前看到 Bootstrap 的横向导航栏有下拉定位的方法,个人觉得十分不错,最终的目录样式应该像这个一样才对。
在这篇文章文章中找到几乎一样的设计思路:Responsive Dropdown Navigation Bar
再来观察一下 HTML 骨架。1
2
3
4
5
6
7
8
9
10
11
12
13<nav>
...
<ul class="nav">
....
<li><a href="#id3">id3</a>
<ul class="nav">
<li><a href="#id4">id4</a></li>
....
</ul>
</li>
....
</ul>
</nav>
SASS骨架
1 | nav { |
可以看到的是我们已经把父级菜单调整好了,需要二次调整的是子级菜单。
也就是nav .nav .nav
的这些。
1 | nav .nav .nav{ |
实现效果:
因为 TOC 插件会将子级菜单持续显示,因此加入一条控制句柄,使得点击父级菜单可以收起对应的子级菜单。
1 | (function($) { |
待优化部分
当然目前还存在一些问题。需要后面找时间解决了。
- 从阅读习惯上来说,顶部的目录栏的菜单应该默认收起,需要时再展开,而不是到一个区间就自动展开。
- 目前点击箭头和内容都是直接跳到父级标题的位置,希望优化成点击箭头展开子级菜单,点击父级标题跳转。
- 从上往下拉的时候目录栏还有位置的跳动,有待优化。
参考资料
- Implementing ScrollSpy with Jekyll to auto-build a table of contents
- Table of Contents plugin for Bootstrap
- Twitter bootstrap 的 affix.js 插件
- margin auto 实现居中,与text-align:center的区别
- text-align - CSS - MDN
- Responsive Dropdown Navigation Bar