Files
ephron-ren-prd/prd-blog-toc-highlight-fix.md

4.2 KiB
Raw Blame History

博客目录高亮逻辑优化

版本: v1.0
日期: 2026-05-06
状态: 已修复


一、问题描述

1.1 现象

博客正文页右侧目录的高亮逻辑存在边界问题当点击小标题H3页面正确滚动到该位置但目录实际高亮的是上方的大标题H2

1.2 复现步骤

  1. 访问博客文章(如 /posts/hermes-chrome-opencode-ai-agent-bug
  2. 找到一个小标题H3和它上方的大标题H2非常接近的章节
  3. 点击目录中的小标题
  4. 观察右侧目录的高亮状态

预期行为:小标题被高亮
实际行为:大标题被高亮

1.3 影响范围

  • 影响所有博客文章页
  • 影响标题间距较小的章节

二、根因分析

2.1 原始代码

function updateTocHighlight() {
    let current = '';
    headings.forEach(heading => {
        const rect = heading.getBoundingClientRect();
        if (rect.top <= 80) {
            current = heading.id;
        }
    });
}

2.2 问题根因

  1. 当点击小标题H3H3 滚动到 rect.top = 80scroll-margin-top
  2. 由于滚动精度问题H3 的 rect.top 可能是 80.581
  3. 原逻辑使用 <= 80 判断H3 不满足条件
  4. 而上方的 H2 满足条件(rect.top <= 80
  5. 结果高亮选中了 H2

核心问题:阈值判断是硬性的,没有考虑滚动精度误差。


三、解决方案

3.1 方案对比

方案 实现 优点 缺点
A. 扩大阈值 <= 82 简单 仍可能有边界问题
B. 距离最近算法 选择距离 80px 最近的标题 精确 稍复杂
C. 滚动后手动设置 点击时直接设置高亮 确定性高 需要额外逻辑

3.2 采用方案:距离最近算法

原理遍历所有标题找到距离目标位置80px最近的那个标题。

实现

function updateTocHighlight() {
    const SCROLL_OFFSET = 80;
    let current = '';
    let minDistance = Infinity;
    
    headings.forEach(heading => {
        const rect = heading.getBoundingClientRect();
        // 只考虑在视口上方或接近顶部的标题
        if (rect.top <= SCROLL_OFFSET + 20) {
            // 计算距离目标位置的绝对值
            const distance = Math.abs(rect.top - SCROLL_OFFSET);
            // 选择距离最近的标题
            if (distance < minDistance) {
                minDistance = distance;
                current = heading.id;
            }
        }
    });
}

优点

  • 精确:选择距离目标位置最近的标题
  • 鲁棒:不受滚动精度影响
  • 直观:符合用户预期

四、实现细节

4.1 修改文件

  • blog/templates/post.html:修改 updateTocHighlight() 函数

4.2 算法说明

  1. 目标位置SCROLL_OFFSET = 80px(导航栏高度 64px + 留白 16px
  2. 候选范围rect.top <= SCROLL_OFFSET + 20(即 <= 100px
  3. 选择标准Math.abs(rect.top - SCROLL_OFFSET) 最小的标题

4.3 边界情况

  • 标题在视口上方rect.top < 0距离计算为 |负数 - 80|,值较大,不会被选中
  • 标题恰好在 80px:距离为 0优先选中
  • 标题在 80px 以下:不满足 <= 100 条件,不参与计算

五、测试验证

5.1 测试用例

编号 测试步骤 预期结果
T-001 点击 H3 标题(与 H2 间距 < 50px H3 被高亮
T-002 点击 H2 标题 H2 被高亮
T-003 滚动页面(不点击) 最接近顶部的标题被高亮
T-004 快速连续点击多个标题 每次点击后高亮正确

5.2 验证方法

  1. 部署后访问博客文章
  2. 找到 H2 和 H3 间距较小的章节
  3. 点击目录中的 H3
  4. 确认 H3 被高亮(而非 H2

六、优先级与排期

优先级 任务 状态
P0 修改高亮算法 已完成
P1 测试验证 待部署后验证

附录

A. 相关文件

  • blog/templates/post.html:目录生成和高亮逻辑

B. 参考资料