TOC 实现
概述
本文档介绍了在目录(TOC)导航中渲染垂直线的正确方法。新的策略将线条渲染从锚点元素移至列表项元素,使用 border-left
替代 ::before
伪元素。
问题分析
旧方法(锚点上的 ::before)存在的问题
- 高度计算问题:线条无法正确延伸覆盖整个列表项高度
- 溢出问题:线条可能超出预期边界
- 嵌套继承复杂:嵌套列表需要复杂的计算
- 定位复杂:需用 calc() 进行绝对定位
- 间隙覆盖难:难以保证线条连续无缝
解决方案:新方法(li 上的 border-left)
主要优势
- 自动高度:边框会随内容高度自动增长
- 无溢出:边框只在列表项内部
- 自然继承:嵌套列表自然继承父级边框
- 实现简单:无需复杂定位或计算
- 无缝连续:父子项线条自然连接
- 响应式友好:适用于动态内容和响应式布局
实现细节
核心 CSS 变更
// 新方法 - 应用于 li 元素
.toc-list-item {
border-left: 2px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
margin-bottom: 0.25rem;
}
.toc-list-item a {
color: #0a55a7;
text-decoration: none;
display: block;
padding: 0.5rem 0;
transition: all 0.2s ease;
line-height: 1.4;
// 无需 position: relative
// 无需 ::before 伪元素
}
// 激活状态样式
.toc-list-item.active {
border-left-color: #0a55a7;
border-left-width: 3px;
}
.toc-list-item.active a {
color: #0a55a7;
font-weight: 600;
}
// 自然嵌套继承
.toc-list-item .toc-list {
margin-top: 0.5rem;
}
.toc-list-item .toc-list .toc-list-item {
margin-left: 0; // 继承父级边框
border-left-color: #d1d5db; // 嵌套项更浅
}
// 顶级项去除边框
.toc-container > #TableOfContents > .toc-list > .toc-list-item {
border-left: none;
padding-left: 0;
margin-left: 0;
}
具体文件修改
1. 修改 assets/scss/templates/_toc.scss
将第 30-61 行替换为:
#TableOfContents {
> .toc-list {
padding-left: 0 !important;
> .toc-list-item {
border-left: none; // 顶级项无边框
padding-left: 0;
margin-left: 0;
}
}
.toc-list {
padding-left: 0;
.toc-list-item {
border-left: 2px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
margin-bottom: 0.25rem;
}
.toc-list-item a {
color: $primary-color;
text-decoration: none;
display: block;
padding: 0.5rem 0;
transition: all 0.2s ease;
line-height: 1.4;
// 移除所有 ::before 伪元素代码
}
.toc-list-item.active {
border-left-color: $primary-color;
border-left-width: 3px;
}
.toc-list-item.active a {
color: $primary-color;
font-weight: 600;
}
// 嵌套继承
.toc-list-item .toc-list .toc-list-item {
margin-left: 0;
border-left-color: #d1d5db;
}
.is-active-link {
font-weight: inherit;
}
}
}
2. 修改 assets/scss/layouts/book/_toc.scss
为书籍目录添加新方法:
#TableOfContents {
padding-left: 0;
overflow-y: auto;
overflow-x: hidden;
max-height: calc(100vh - 168px);
height: auto;
.toc-list-item {
border-left: 2px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
margin-bottom: 0.25rem;
display: block;
word-break: break-word;
}
.toc-list-item a {
display: block;
line-height: 1.5;
font-size: 0.9rem;
color: $text-color-dark;
text-decoration: none;
padding: 0.5rem 0;
transition: all 0.2s ease;
}
.toc-list-item a:hover {
color: $primary-color;
}
.toc-list-item.active {
border-left-color: $primary-color;
border-left-width: 3px;
}
.toc-list-item.active a,
.toc-list-item a.active {
color: $primary-color;
font-weight: 700;
}
// 顶级项去除边框
> ul > .toc-list-item {
border-left: none;
padding-left: 0;
margin-left: 0;
}
// 嵌套继承
.toc-list-item .toc-list .toc-list-item {
margin-left: 0;
border-left-color: #d1d5db;
}
}
3. 修改移动端 TOC 样式
在移动端 TOC 区块应用同样方法:
.mobile-toc-modal-body {
#TableOfContents {
ul {
list-style: none;
padding: 0;
margin: 0;
.toc-list-item {
border-left: 2px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
margin-bottom: 0.25rem;
border-bottom: 1px solid var(--card-border, #f0f0f0);
&:last-child {
border-bottom: none;
}
a {
display: block;
padding: 1rem 0;
color: var(--text-color, #495057);
text-decoration: none;
transition: all 0.2s ease;
font-size: 0.9rem;
line-height: 1.5;
&:hover {
background: var(--card-border, #f8f9fa);
color: var(--accent-color, #0a55a7);
}
&.active {
color: var(--accent-color, #0a55a7);
font-weight: 500;
}
}
&.active {
border-left-color: var(--accent-color, #0a55a7);
border-left-width: 3px;
}
// 嵌套继承
.toc-list .toc-list-item {
margin-left: 0;
border-left-color: #d1d5db;
}
}
}
}
}
验证点
新实现应满足:
- ✅ 线条延伸至列表项全高
- ✅ 无溢出
- ✅ 嵌套列表自然继承边框
- ✅ 激活状态正常
- ✅ 响应式表现良好
- ✅ CSS 简化,无复杂计算
- ✅ 支持动态内容
- ✅ 父子项线条无缝衔接
测试
原型文件展示:
- 基础结构 - 简单嵌套 TOC,含激活状态
- 深层嵌套 - 4 层及以上嵌套
- 长内容 - 多行标题自动换行
- 混合层级 - 多种嵌套模式
- 边界情况 - 空列表、单项、直接深层嵌套
- 交互行为 - 动态状态切换
优势总结
新方法消除了伪元素定位和计算的复杂性,带来:
- 更可靠的线条渲染
- 更佳的响应式表现
- 更易维护
- 自然嵌套继承
- 各种场景下一致的视觉效果
此方法确保 TOC 导航中的垂直线条无论内容长度或嵌套深度都能保持健壮、易维护和一致的视觉表现。