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 的规则不太一样。我把自己常用的几个平台整理了一个对照表:

规则GitHubGitLabCSDNPandoc
英文标题Getting Startedgetting-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 最让人头疼的部分。我把自己实际用过的几个平台的关键差异列出来:

特性GitHubGitLabCSDNJekyllPandoc
标题自动 ID
{#id} 自定义
<a id=""> 手动✅(仅 id)
<a name="">❌(被过滤)
跨页面锚点
中文标题 ID保留中文保留中文保留中文取决于配置保留中文

从这个表里可以看出一个规律:<a id="xxx"> 的方式是最通用的,在所有平台上都能工作。如果你不确定目标平台支持什么,就用这种方式。

常见问题与排错

锚点链接不生效

这是最常遇到的问题。检查以下几点:

  1. ID 匹配问题:链接里的 ID 必须和目标 ID 完全一致,包括大小写。#Getting-Started#getting-started 是不同的。

  2. 平台不支持:确认你用的平台是否支持标题自动生成 ID。有些老的 Markdown 编辑器不生成。

  3. 渲染后查看:锚点只在渲染后的 HTML 中有效,在 Markdown 源码里直接看是不会有跳转效果的。如果你的编辑器支持实时预览,切到预览模式试试。

  4. 空格和特殊字符:如果你链接的是一个带空格或特殊字符的标题,记得按平台的规则转换。GitHub 上 ## Hello World! 对应的是 #hello-world,不是 #hello-world!

中文标题的锚点怎么写

中文标题的锚点在 GitHub、GitLab、CSDN 上基本都是直接保留中文字符:

## 使用说明

[跳到使用说明](#使用说明)

但这里有个小陷阱:有些渲染器会把中文标点去掉,有些会保留。比如 ## 安装(推荐) 这个标题,有的平台生成 #安装推荐(去掉了括号),有的生成 #安装(推荐)(保留括号)。如果你发现中文标题的锚点不工作,最稳妥的办法是在浏览器里打开渲染后的页面,右键检查元素,看看实际生成的 id 值到底是什么。

重复标题怎么处理

前面提到过,如果同一个页面里有多个相同的标题,大多数平台会给后面的追加数字后缀:

## 配置           → #配置
## 配置           → #配置-1
## 配置           → #配置-2

但这不是所有平台都这样处理。所以最好还是避免同名标题,或者用自定义锚点来区分。

实战:给文章加一个可点击的目录

学完了锚点的各种写法,来做一个实际的东西——手动给文章创建一个目录,点击每个条目就能跳到对应章节。

## 目录

- [简介](#简介)
- [安装](#安装)
- [配置](#配置)
- [常见问题](#常见问题)

## 简介

这里是简介的内容...

## 安装

安装步骤...

## 配置

配置方法...

## 常见问题

Q&A...

就这么简单。用 Markdown 列表 + 锚点链接就行。

如果你的文档很长,还可以在每个章节的末尾加一个"返回顶部"的链接:

## 简介

简介内容...

[↑ 返回目录](#目录)

## 安装

安装步骤...

[↑ 返回目录](#目录)

说实话语法本身没几行,但坑都在细节里。不同平台的行为差异、中文标题的 ID 生成规则、重复标题的后缀处理——这些才是真正让人抓狂的地方。我的建议是:先确定你主要用什么平台,然后记住那个平台的规则就够了。如果要跨平台,就用 <a id="xxx"> 这个最通用的方案。

参考来源

  1. GitHub Flavored Markdown Spec — GitHub 官方 Markdown 规范
  2. Markdown Guide — Hacks — Markdown Guide 项目,锚点 workaround 章节
  3. CommonMark Spec Discussion: Anchors in Markdown — CommonMark 规范层面关于锚点的讨论
  4. Pandoc User's Guide — Heading Identifiers — Pandoc 官方文档中的标题 ID 说明
  5. markdown-it-anchor (npm) — 开发者侧的锚点渲染方案参考