前置知识: Markdown

锚点跳转

5 min中级

锚点机制:GitHub 标题 slug 规则、自定义锚点、页内与跨文档跳转的写法与排错。

认知导入(Layer 1 进阶层) 前置知识:017 链接与图片(锚点链接就是目的地为 #片段 的链接)。 边界说明:Markdown 标题会自动生成锚点(HTML id),锚点规则由各渲染器自行定义——本篇以 GitHub 规则为主线,其余平台给出差异提示。锚点跳转失效的原因九成是”slug 算错了”。 强制练习:写一个中文标题和一个英文标题,各生成一次锚点并链接过去,在 GitHub 预览验证能否跳转。

1. 锚点是什么

锚点是页面内的定位标记,对应 HTML 元素的 id 属性。URL 中 # 后面的部分叫片段标识符(fragment),浏览器加载页面后滚动到对应 id 的元素:

URL: https://example.com/docs#installation
                              ↑
                          片段标识符,对应 id="installation" 的元素

Markdown 写不了 <a name> 也能玩锚点——渲染器会为每个标题自动生成 id,于是”链接到某标题”只需要知道标题的 slug(URL 化的字符串)。

2. GitHub 的标题 slug 规则

GitHub 的生成算法(对仓库文件、Issue、评论一致)分四步:

  1. 转小写(英文部分);
  2. 删除所有”非文字、非空格、非连字符”的字符(标点、符号被剔除);
  3. 空格替换为连字符 -(每个空格一个连字符,不合并);
  4. 重复的 slug 追加 -1、-2 后缀。
标题源码生成的锚点说明
## Getting Started#getting-started小写 + 空格变连字符
## What's New?#whats-new撇号与问号被删除
## API v2.0#api-v20点号被删除
## C++ 编程#c-编程+ 被删除,中文保留
## A --- B#a-----b原有连字符保留、空格各变一个 -
## 安装指南#安装指南中文原样保留
第二个 ## 示例#示例-1重复 slug 加序号

两个高频误区:

  • 连字符不会被合并:A --- B 这类标题生成 a-----b(原连字符照留、空格逐个转换),凭感觉写 #a-b 必然跳转失败;
  • emoji 也参与 slug:GitHub 会把 emoji 转成短代码文本(如 ## 🚀 发布 生成 #-发布),这种标题不要依赖锚点链接。

GitLab 的规则与 GitHub 基本兼容;Obsidian、Hugo、VuePress 等各有细节差异(是否剔除中文、是否转拼音等)。跨平台文档的标题尽量用”字母数字 + 单个空格”,slug 才有一致性可言。

3. 页内跳转

3.1 链接到标题

## 目录

- [安装](#安装)
- [快速开始](#getting-started)

## 安装

## Getting Started

链接目的地就是 # + slug,与普通链接语法完全一致(见 markdown/050-LinkImage)。

3.2 中文标题直接写中文锚点

GitHub 保留中文字符,链接里直接写中文即可,浏览器会做 URL 编码:

[跳到安装步骤](#安装步骤)

## 安装步骤

内部工具链(某些静态站点生成器、IDE 插件)对中文 slug 处理不一致,纯内部文档没问题,公开发布前在目标平台逐个点一遍。

4. 自定义锚点

自动 slug 会随标题文字变化,且”重复标题加 -1”不可控。需要稳定锚点时显式声明。

4.1 HTML 锚点标签(最通用)

<a name="changelog-v2"></a>

## 更新日志

[跳到更新日志](#changelog-v2)

空锚点标签放在目标位置的前一行,不影响渲染。GitHub 官方文档推荐 <a name="...">;多数其他渲染器(站点生成器、IDE 预览)同样支持 id 属性。需要跨渲染器稳定的场景可以两个都写:<a id="x" name="x"></a>。

4.2 扩展属性语法(仅部分渲染器)

## 更新日志 {#changelog-v2}

{#id} 是 Pandoc、kramdown、PHP Markdown Extra 等的扩展;GitHub 不支持(整段被当标题文字或忽略),只在自己掌控渲染管线的项目里使用。

5. 跨文档与跨页锚点

<!-- 链接到同仓库其他文件的某章节(GitHub 支持) -->
[参见安装文档](./install.md#安装步骤)

<!-- 链接到渲染后的 HTML 页面锚点 -->
[查看页面](https://example.com/docs/page.html#section)

<!-- 站点根路径写法(仅站点环境有效,GitHub 仓库内无效) -->
[文档首页](/docs/index.md#top)

跨文档锚点的生效条件比页内链接苛刻:目标文件必须被同一渲染管线处理,且 slug 规则一致。GitHub 上仓库内 .md 互链是支持的;一旦文档导出为 PDF 或迁移站点,跨文件锚点往往需要重建。

6. 实战场景

6.1 返回顶部

<a name="top"></a>

# 长文档标题

(大量内容……)

[返回顶部](#top)

6.2 顶部横向导航

[安装](#安装) | [配置](#配置) | [FAQ](#faq)

6.3 长文档交叉引用

在正文中引用其他章节时用锚点链接而非”见上文第 3 节”这类模糊表述,章节增删后链接要么仍指向正确位置、要么在死链检查中显式报错:

环境变量配置见[高级配置](#高级配置)。

7. 排错与验证

锚点跳转失效按固定顺序排查:

现象原因解决
点击无反应slug 不匹配(大小写、连字符数)按 GitHub 规则重算 slug
标题改了链接失效slug 随标题文字变化关键位置用 <a name> 固定
重复标题跳错位置自动加了 -1 后缀用自定义锚点区分
中文锚点在别处失效目标渲染器 slug 规则不同统一用英文标题或显式 id
跨文件锚点失效平台不支持或路径错误确认平台支持 .md#锚点 互链

验证工具:

# markdown-link-check 可以检查页内锚点(配置 page 下 anchor 校验)
npx markdown-link-check README.md

浏览器端调试:在渲染页面控制台列出全部 id,与目录链接逐一比对:

document.querySelectorAll('[id]').forEach((el) =>
  console.log(`#${el.id} → ${el.textContent.trim().slice(0, 30)}`)
);

8. 最佳实践

  1. 标题命名优先”字母数字 + 单个空格”:slug 在各平台表现一致,跨平台最稳。
  2. 被外部引用的位置用显式锚点:<a name> 不随标题改动失效,适合 FAQ、API 条目等深链目标。
  3. 避免在标题里放代码、emoji 与标点:锚点难看且易错。
  4. 重复标题必然产生 -1 后缀:要么改标题,要么自定义锚点。
  5. 发布前逐个点击:锚点错误不会在任何检查里自动暴露(除非配置了死链检查),人工点击是最便宜的验证。

小结

  • 初学者要点:标题自动生成锚点;页内跳转写 [文字](#标题);GitHub 规则是”小写、删标点、空格变连字符”;中文标题直接写中文锚点。
  • 进阶注意:连字符不合并、重复标题加 -1,凭感觉拼 slug 是跳转失败的第一原因;需要稳定深链用 <a name> 显式锚点;{#id} 语法 GitHub 不支持;跨文档锚点依赖同渲染管线,导出或迁移时重建。