Markdown 锚点语法完全指南
写长文档的时候,你肯定遇到过这种需求:开头放个目录,点击某个章节名称就直接跳到对应位置。这就是 markdown 锚点(anchor)干的事。
说实话这个功能本身不难,但坑在于——不同平台对锚点的处理方式差别挺大的。GitHub 上一套规则,CSDN 上又一套,Pandoc 又不一样。这篇文章就把 markdown anchor 的各种用法和平台差异都梳理清楚。
什么是 Markdown 锚点
锚点的概念来自 HTML。在网页里,给某个位置设一个 id,然后用 #id 格式的链接就能跳到那个位置。Markdown 锚点做的事情一样,只不过写法上做了一些简化。
举个最直白的例子:
[跳转到安装指南](#installation)
## Installation
这里写安装步骤...点击"跳转到安装指南"这个链接,页面就会滚动到 ## Installation 这个标题的位置。大多数 Markdown 渲染器会自动根据标题文字生成对应的 ID,你只需要用 # 加上这个 ID 就能链接过去。
顺便说一句,如果你还想了解更广泛的链接写法(比如外部链接、图片链接),可以看看 Markdown 链接语法这篇文章。本文只聚焦锚点这一种特殊的链接方式。
标题自动生成锚点
这是最常用的方式,也是大多数平台的默认行为——你写一个标题,渲染器自动给它生成一个 ID,然后你就能用 #id 跳过去。
基本用法
## 快速开始
点击[这里](#快速开始)回到开头。在上面这个例子中,## 快速开始 会被渲染成一个带有 id 属性的 HTML 标题,你用 #快速开始 就能链接到它。
不过,不同平台生成 ID 的规则不太一样。我把自己常用的几个平台整理了一个对照表:
| 规则 | GitHub | GitLab | CSDN | Pandoc |
|---|---|---|---|---|
| 英文标题 | Getting Started → getting-started | 同 GitHub | 同 GitHub | 同 GitHub |
| 空格处理 | 替换为 - | 同左 | 同左 | 同左 |
| 大写字母 | 转小写 | 同左 | 同左 | 同左 |
| 特殊字符 | 移除(保留 -) | 同左 | 同左 | 同左 |
| 中文标题 | 保留原字符 | 保留原字符 | 保留原字符 | 保留原字符 |
| 重复标题 | 追加 -1、-2 | 同左 | 同左 | 同左 |
GitHub 的标题 ID 生成规则
GitHub 是大家用得最多的 Markdown 渲染平台之一,它的规则比较有代表性:
## What's New? → id="whats-new"
## Getting Started → id="getting-started"
## Let's try it! → id="lets-try-it"
## 3. Installation → id="3-installation"规则总结一下就是:全转小写,空格变连字符,标点符号去掉(连字符本身保留),数字保留。如果两个标题生成了一样的 ID,后面那个会自动追加 -1、-2 这样递增的后缀。
我之前在 GitHub 上写一个项目的 README,里面有两个三级标题都叫 "配置",一个在 Linux 章节,一个在 macOS 章节。结果两个都生成了 #配置,GitHub 就自动把第二个改成了 #配置-1。当时我还纳闷为什么链接跳错了位置,后来才发现是重复标题的问题。所以如果你的文档里有同名的标题,最好手动给其中一个加个区分。
手动创建锚点
标题自动生成的锚点能覆盖大部分场景,但有时候你想跳到的位置并不是一个标题——比如表格中间、代码块旁边、或者一段话的某个位置。这时候就需要手动创建锚点了。
用 HTML 的 <a> 或 <span> 标签
Markdown 本身没有专门创建锚点的语法,但几乎所有 Markdown 渲染器都支持嵌入 HTML。所以最通用的方法就是用 HTML 标签的 id 属性:
<a id="important-note"></a>
> 这是一段特别重要的注意事项,我想让读者能直接跳到这里。
点击[这里](#important-note)跳到上面的注意事项。<a> 和 <span> 都可以,效果一样。我一般用 <a>,因为语义上更准确(它就是一个锚点)。有些人喜欢用 <div id="xxx">,也行,但 <div> 是块级元素,在某些场景下会影响布局。
需要注意的是,CSDN 的编辑器会把 <a> 标签的 name 属性过滤掉,只保留 id 属性。所以如果你要在 CSDN 上用,务必用 id 而不是 name。
自定义标题锚点 ID
有些 Markdown 扩展支持直接给标题指定自定义 ID,这样就不依赖自动生成的规则了。
{#id} 语法
这是最常见的一种写法,源自 Markdown Extra 和 Pandoc:
## 安装步骤 {#install-steps}
详细安装内容...
[跳到安装步骤](#install-steps){#install-steps} 写在标题文字后面,用花括号包起来。这样不管标题本身是什么文字,生成的锚点 ID 都是 install-steps。
支持的渲染器包括:
- Pandoc:完整支持
- Kramdown(Jekyll 默认):完整支持
- PHP Markdown Extra:完整支持
- R Markdown:支持
- GitHub:不支持,
{#id}会被当作标题文字的一部分
GitHub 不支持 {#id} 这一点很多人踩坑。如果你想给 GitHub 上的文档用自定义锚点,只能用前面说的 HTML <a id="xxx"> 方式。
Pandoc 的另一种写法
Pandoc 还支持另一种自定义锚点的方式,不过这个就比较冷门了:
[跳到这里]{#custom-target}
一些内容...这种行内锚点只在 Pandoc 中有效,其他平台基本不支持。
锚点链接的完整语法
了解了怎么创建锚点之后,来看看怎么写链接指向它们。
页内跳转
最基本的用法,在当前页面内跳转:
[跳到常见问题](#常见问题)
...(很多内容)...
## 常见问题链接地址就是 # 加上目标的 ID 值,不需要写页面路径。
跨页面跳转
锚点不仅可以跳当前页面的位置,还能跳到另一个页面的某个位置:
[查看另一篇文章的安装章节](./installation.md#linux)
[查看官网的 API 文档](https://example.com/docs#api-reference)写法就是在 URL 后面直接拼 #id。对本地 Markdown 文件,用相对路径加 #;对网页 URL,也是直接追加 #。
我之前在写一个系列教程的时候,每篇文章开头都放了一个"上一篇 / 下一篇"的导航,同时还有个链接直接跳到下一篇的特定章节。这种跨页面锚点在多文档项目里特别好使。
用 HTML 的 <a> 标签写链接
虽然不常用,但你也可以用 HTML 的方式写锚点链接:
<a href="#常见问题">跳到常见问题</a>效果跟 Markdown 链接语法一样,只是在某些不支持 Markdown 锚点的平台上,HTML 写法反而能正常工作。
平台兼容性差异
这是写 markdown anchor 最让人头疼的部分。我把自己实际用过的几个平台的关键差异列出来:
| 特性 | GitHub | GitLab | CSDN | Jekyll | Pandoc |
|---|---|---|---|---|---|
| 标题自动 ID | ✅ | ✅ | ✅ | ✅ | ✅ |
{#id} 自定义 | ❌ | ✅ | ❌ | ✅ | ✅ |
<a id=""> 手动 | ✅ | ✅ | ✅(仅 id) | ✅ | ✅ |
<a name=""> | ❌ | ✅ | ❌(被过滤) | ✅ | ✅ |
| 跨页面锚点 | ✅ | ✅ | ✅ | ✅ | ✅ |
| 中文标题 ID | 保留中文 | 保留中文 | 保留中文 | 取决于配置 | 保留中文 |
从这个表里可以看出一个规律:用 <a id="xxx"> 的方式是最通用的,在所有平台上都能工作。如果你不确定目标平台支持什么,就用这种方式。
常见问题与排错
锚点链接不生效
这是最常遇到的问题。检查以下几点:
ID 匹配问题:链接里的 ID 必须和目标 ID 完全一致,包括大小写。
#Getting-Started和#getting-started是不同的。平台不支持:确认你用的平台是否支持标题自动生成 ID。有些老的 Markdown 编辑器不生成。
渲染后查看:锚点只在渲染后的 HTML 中有效,在 Markdown 源码里直接看是不会有跳转效果的。如果你的编辑器支持实时预览,切到预览模式试试。
空格和特殊字符:如果你链接的是一个带空格或特殊字符的标题,记得按平台的规则转换。GitHub 上
## Hello World!对应的是#hello-world,不是#hello-world!。
中文标题的锚点怎么写
中文标题的锚点在 GitHub、GitLab、CSDN 上基本都是直接保留中文字符:
## 使用说明
[跳到使用说明](#使用说明)但这里有个小陷阱:有些渲染器会把中文标点去掉,有些会保留。比如 ## 安装(推荐) 这个标题,有的平台生成 #安装推荐(去掉了括号),有的生成 #安装(推荐)(保留括号)。如果你发现中文标题的锚点不工作,最稳妥的办法是在浏览器里打开渲染后的页面,右键检查元素,看看实际生成的 id 值到底是什么。
重复标题怎么处理
前面提到过,如果同一个页面里有多个相同的标题,大多数平台会给后面的追加数字后缀:
## 配置 → #配置
## 配置 → #配置-1
## 配置 → #配置-2但这不是所有平台都这样处理。所以最好还是避免同名标题,或者用自定义锚点来区分。
实战:给文章加一个可点击的目录
学完了锚点的各种写法,来做一个实际的东西——手动给文章创建一个目录,点击每个条目就能跳到对应章节。
## 目录
- [简介](#简介)
- [安装](#安装)
- [配置](#配置)
- [常见问题](#常见问题)
## 简介
这里是简介的内容...
## 安装
安装步骤...
## 配置
配置方法...
## 常见问题
Q&A...就这么简单。用 Markdown 列表 + 锚点链接就行。
如果你的文档很长,还可以在每个章节的末尾加一个"返回顶部"的链接:
## 简介
简介内容...
[↑ 返回目录](#目录)
## 安装
安装步骤...
[↑ 返回目录](#目录)说实话语法本身没几行,但坑都在细节里。不同平台的行为差异、中文标题的 ID 生成规则、重复标题的后缀处理——这些才是真正让人抓狂的地方。我的建议是:先确定你主要用什么平台,然后记住那个平台的规则就够了。如果要跨平台,就用 <a id="xxx"> 这个最通用的方案。
参考来源
- GitHub Flavored Markdown Spec — GitHub 官方 Markdown 规范
- Markdown Guide — Hacks — Markdown Guide 项目,锚点 workaround 章节
- CommonMark Spec Discussion: Anchors in Markdown — CommonMark 规范层面关于锚点的讨论
- Pandoc User's Guide — Heading Identifiers — Pandoc 官方文档中的标题 ID 说明
- markdown-it-anchor (npm) — 开发者侧的锚点渲染方案参考