Markdown 与 HTML 混合使用完全指南
Markdown 写起来很舒服,但总会碰到它搞不定的排版需求——比如让图片居中、设置文字颜色、做个可折叠的内容区域,或者用 HTML 表格实现跨行合并。这时候你可能会搜 "markdown in html" 或 "html in markdown",想知道这两种格式能不能混着用。
答案是肯定的。Markdown 的设计者 John Gruber 在最初设计时就明确说过:HTML 是 Markdown 的发布格式,Markdown 不是 HTML 的替代品,而是它的补充。所以在 Markdown 文件里直接写 HTML 标签,不是什么 hack,而是被官方鼓励的做法。
不过这里面有不少细节值得注意。比如,行内标签和块级标签的行为完全不同;有些平台支持 markdown="1" 属性让 HTML 标签内部的 Markdown 语法也能生效,但有些平台完全忽略它。我在这篇文章里会把这些规则和差异讲清楚,并分享一些实际使用中的经验。
核心规则:行内标签 vs 块级标签
理解 Markdown 和 HTML 混用的关键,在于搞清楚两类 HTML 标签在 Markdown 中的不同行为。
行内标签(Inline Tags)—— 随便用,Markdown 正常解析
行内标签也叫 span-level 标签,比如 <span>、<em>、<strong>、<a>、<code>、<br> 等。这些标签可以在 Markdown 文本中任意位置使用,而且不影响周围的 Markdown 语法解析。
这是一个 <span style="color:red">红色文字</span> 的例子。
这行字里 <em>这里用 HTML 斜体</em> 而 **这里用 Markdown 加粗**,两者和平共处。
点击 <a href="https://example.com" target="_blank">这个链接</a> 在新窗口打开。上面的写法在几乎所有 Markdown 解析器中都能正常工作,因为行内标签被当作 Markdown 文本的一部分来处理。
块级标签(Block-level Tags)—— 内部 Markdown 会被忽略
块级标签是另一回事。<div>、<table>、<p>、<pre>、<form> 这些标签一旦出现,Markdown 解析器会认为这个区域不是 Markdown 了,里面的内容直接按原始 HTML 处理。
下面这样写,**加粗** 不会生效:
<div>
这段话里的 **加粗** 不会被渲染,*斜体* 也不会。
</div>这个行为是 Markdown 规范(包括原始规范和 CommonMark)的明确定义,不是某个解析器的 bug。Markdown 解析器在遇到块级 HTML 标签时,会停止解析,直接把原始 HTML 原样输出。
我第一次碰到这个问题是在写 GitHub README 的时候。当时想用 <div> 做一个两列布局,结果发现 div 里面所有的 Markdown 链接和格式全部失效了,变成了一坨纯文本。后来才知道,这是 Markdown 解析器的设计行为。
用空行隔开块级标签和 Markdown 内容
有一个容易忽略的规则:块级 HTML 标签的前后需要用空行与 Markdown 内容隔开。如果不隔开,有些解析器会把 HTML 标签当作普通文本来处理。
<!-- 正确写法:前后有空行 -->
前面的 Markdown 内容。
<div>
<p>HTML 块级内容</p>
</div>
后面的 Markdown 内容。另外,块级 HTML 标签不要用空格或制表符缩进。如果缩进了 4 个空格或 1 个制表符,Markdown 会把它当成代码块来处理。
常见用法:用 HTML 补充 Markdown 做不到的事
既然 Markdown 支持嵌入 HTML,那具体有哪些场景会用到?这里列几个最常见的。
控制图片大小和位置
Markdown 的图片语法  没有指定尺寸和对齐方式的选项。如果你想控制图片的显示大小或者让它居中,就得用 HTML。
<!-- 设置图片宽度 -->
<img src="image.png" width="300" alt="示例图片">
<!-- 图片居中 -->
<div align="center">
<img src="image.png" width="80%" alt="居中图片">
</div>在 GitHub 上,<img> 标签的 width 和 height 属性是支持得比较好的。不过 align 属性属于旧式 HTML,有些平台可能会忽略,更现代的做法是用 CSS 的 style 属性。
文字颜色和样式
Markdown 没有设置文字颜色的语法。你可以用 <span> 配合 style 来实现:
<span style="color: red;">红色文字</span>
<span style="color: #0066cc; font-size: 18px;">蓝色大字</span>
<span style="background-color: yellow;">黄底高亮</span>说实话,这种方式在纯 Markdown 编辑器里看着挺别扭的,但渲染出来的效果确实能用。我的建议是,如果你只是偶尔需要改个颜色,用 <span> 就够了;如果整个文档都需要复杂的样式控制,那可能应该直接写 HTML 而不是 Markdown。
可折叠内容(details/summary)
<details> 和 <summary> 这两个 HTML 标签在 GitHub、GitLab、CSDN 等平台上支持得很好,可以做出可折叠/展开的内容区域,非常适合放一些补充信息或长代码块。
<details>
<summary>点击展开查看详细代码</summary>
```python
def hello():
print("Hello, World!")
注意 `<summary>` 标签里的文字会作为折叠区域的标题显示。这个技巧在做 README 文档时特别实用,把安装步骤或常见问题折叠起来,页面看起来清爽很多。
### 键盘按键样式
如果你在写技术文档,可能需要展示键盘快捷键。`<kbd>` 标签正好派上用场:
```markdown
按 <kbd>Ctrl</kbd> + <kbd>C</kbd> 复制,<kbd>Ctrl</kbd> + <kbd>V</kbd> 粘贴。大多数 Markdown 渲染器会把 <kbd> 显示为带边框的小方块,看起来像真实的键盘按键。
页内锚点跳转
Markdown 的链接语法 [文字](#锚点) 支持页内跳转,但你需要先定义锚点位置。可以用 HTML 的 id 属性来实现:
<div id="section-1"></div>
## 第一个章节
这里是章节内容...
[跳转到第一个章节](#section-1)在 GitHub 上,标题会自动生成锚点(基于标题文本),所以这个技巧主要用于非标题位置。
进阶技巧:让 HTML 标签内部的 Markdown 也生效
前面说了,块级 HTML 标签内部的 Markdown 语法默认不被解析。但有些场景确实需要在 HTML 标签里写 Markdown,比如用 <div> 做布局的同时还想用 Markdown 的链接和格式。
方案一:加空行(CommonMark 规范)
CommonMark 规范(GitHub、GitLab、很多现代解析器使用)支持一种方式:在 HTML 标签和内容之间加空行,让解析器知道里面的内容应该当作 Markdown 来处理。
<div>
这段话里的 **加粗** 会正常渲染。
- 列表项一
- 列表项二
</div>关键是开始标签 <div> 后面、内容前面,以及内容后面、结束标签 </div> 前面,都要有空行。我实测在 GitHub 上这个方式是生效的。
方案二:markdown 属性(Python-Markdown、Markdown Extra)
Python-Markdown 的 md_in_html 扩展和 PHP Markdown Extra 支持在 HTML 标签上添加 markdown 属性,明确告诉解析器内部内容按 Markdown 处理。
<div markdown="1">
这里的 **Markdown** 会正常渲染。
</div>markdown 属性支持三种值:
| 值 | 效果 |
|---|---|
"1" | 使用标签的默认行为(div 用 block 模式,p 用 span 模式) |
"block" | 强制块级模式,段落、标题、列表都会正常渲染 |
"span" | 强制行内模式,只渲染加粗、斜体、链接等行内语法 |
需要注意的是,这个属性不是标准 Markdown 规范的一部分,只有在支持它的解析器上才能用。比如 GitHub 的渲染器就不支持这个属性(至少目前不支持),但 GitHub Pages 的 Jekyll/Kramdown 环境是支持的。
方案三:用 <span> 替代 <div>
因为行内标签内部的 Markdown 不受影响,一个取巧的办法是用 <span> 代替 <div>,然后通过 CSS 让它表现为块级元素。
<span style="display: block; border: 1px solid #ccc; padding: 10px;">
这段话里的 **Markdown** 会正常渲染,因为 span 是行内标签。
</span>这个方法兼容性比较好,但语义上不够严谨——<span> 语义上是行内元素,强制改成块级在可访问性方面不太好。
平台兼容性对比
不同平台对 Markdown 中 HTML 的支持程度差异很大。我根据实际测试和官方文档整理了下面的对比表。
| 功能 | GitHub | GitLab | Jekyll/Kramdown | Python-Markdown | Obsidian | Typora |
|---|---|---|---|---|---|---|
| 行内 HTML 标签 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
| 块级 HTML 标签 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
| 块级标签内加空行解析 Markdown | 支持 | 支持 | 支持 | 不支持 | 不支持 | 部分支持 |
markdown="1" 属性 | 不支持 | 不支持 | 支持 | 支持(需扩展) | 不支持 | 不支持 |
<details>/<summary> | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
<img> 设置 width/height | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
<span style="color:"> | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |
<iframe> | 不支持 | 不支持 | 支持 | 支持 | 部分支持 | 支持 |
<script> | 不支持 | 不支持 | 不支持 | 不支持 | 不支持 | 不支持 |
数据来源:各平台官方文档 + Stack Overflow 高赞回答 + 实际测试。GitHub 的 HTML 白名单可在 github/markup 项目中查看。
几个值得注意的点:
- GitHub 出于安全考虑,对可用的 HTML 标签有白名单限制。
<script>、<iframe>、<style>这些标签会被直接过滤掉。 - Obsidian 内部使用自己的 Markdown 解析器,对 HTML 的支持有其特殊性。比如在 Obsidian 里不能在 HTML 块中使用 Markdown 语法,但可以通过 DataviewJS 插件间接实现。
- Jekyll 的 Kramdown 解析器对 HTML in Markdown 的支持是最全面的,
markdown="1"属性和块级标签内加空行两种方式都支持。
实际应用场景与示例
场景一:GitHub README 中让图片居中
这是我在做开源项目时经常用的写法:
<div align="center">

**一个简洁的项目描述**
[](https://github.com/user/repo)
</div>在 GitHub 上,这个写法能正常工作是因为 <div align="center"> 后面的空行让解析器把内容当作 Markdown 来处理。顺便说一句,<div align="center"> 虽然用了过时的 align 属性,但在 GitHub 上这是最可靠的居中方式。
场景二:在 Markdown 中嵌入复杂表格
Markdown 表格不支持跨行跨列(rowspan/colspan)。当你需要这种效果时,可以直接用 HTML 表格:
<table>
<tr>
<th>功能</th>
<th>免费版</th>
<th>专业版</th>
</tr>
<tr>
<td rowspan="2">存储空间</td>
<td>5GB</td>
<td>100GB</td>
</tr>
<tr>
<td colspan="2">可扩展至 1TB(仅专业版)</td>
</tr>
</table>场景三:用 CSS 实现多列布局
在支持 style 属性的平台上(比如自己的博客或文档站),可以用 CSS 实现简单的多列排版:
<div style="display: flex; gap: 20px;">
<div style="flex: 1;">
### 左列内容
- 要点一
- 要点二
</div>
<div style="flex: 1;">
### 右列内容
- 要点三
- 要点四
</div>
</div>不过这种写法在 GitHub 上不一定生效,因为 GitHub 会过滤掉 style 属性中的部分 CSS 规则。
常见问题
Markdown 文件中能直接写 CSS 吗?
可以,但支持程度有限。你可以用 <style> 标签来定义样式,不过很多平台(特别是 GitHub)会把它过滤掉。在自己的博客或文档站点上通常没问题:
<style>
.custom-box {
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
}
</style>
<div class="custom-box">
这段文字会有自定义的边框和内边距。
</div>为什么我的 HTML 标签在 GitHub 上不生效?
最常见的原因有两个:一是 GitHub 有 HTML 白名单,<script>、<iframe>、<form> 等标签会被直接移除;二是 GitHub 会过滤掉 style 属性中的部分 CSS(比如 position、z-index 等可能影响页面布局的属性)。如果你需要完全控制 HTML 和 CSS,建议使用 GitHub Pages 而不是直接在 README 中写。
<font> 标签还能用吗?
技术上可以,但不推荐。<font> 在 HTML5 中已被弃用(deprecated),虽然浏览器仍然支持渲染。建议使用 <span style="color: red;"> 的方式来替代。
安全注意事项
在 Markdown 中嵌入 HTML 时,安全性是一个需要注意的方面,特别是在用户可以提交 Markdown 内容的平台上。
XSS 风险
如果在 Markdown 中允许任意 HTML,就可能存在 XSS(跨站脚本攻击)的风险。比如:
<!-- 危险示例,不要在生产环境中使用 -->
<img src="x" onerror="alert('XSS')">
<a href="javascript:alert('XSS')">点击我</a>大多数平台会过滤掉 onerror、onclick 这类事件属性和 javascript: 协议的链接。但如果你自己在搭建 Markdown 渲染系统,需要确保使用 XSS 过滤库(比如 DOMPurify)来清理 HTML 内容。
内容安全策略
如果你在用 Markdown 解析库(如 marked.js、markdown-it)构建自己的应用,建议:
- 始终启用 HTML sanitization(HTML 清理)选项
- 使用白名单而非黑名单来过滤 HTML 标签和属性
- 不要信任用户提交的 Markdown 中的 HTML 内容
总结
Markdown 与 HTML 的混合使用并不复杂,记住几个要点就够了:
- 行内 HTML 标签(
<span>、<em>、<a>等)可以在 Markdown 中随意使用,不影响 Markdown 语法解析 - 块级 HTML 标签(
<div>、<table>、<p>等)内部的 Markdown 语法默认不被解析 - 如果需要在块级标签内使用 Markdown,可以尝试加空行(CommonMark 规范)或
markdown="1"属性(Python-Markdown / Kramdown 支持) - 不同平台的 HTML 支持程度差异很大,使用前最好先在目标平台上测试
- 安全性不能忽视,特别是在接受用户提交的 Markdown 内容时
说到底,Markdown 和 HTML 不是对立的。Markdown 处理大部分写作场景已经足够好了,偶尔需要更精细的控制时就用 HTML 来补充。两者配合使用,才是最实用的方式。
参考资料
- Markdown: Syntax - Daring Fireball — Markdown 官方语法说明
- CommonMark Spec — CommonMark 规范
- Markdown Guide - Basic Syntax — Markdown 基础语法参考
- Python-Markdown: md_in_html Extension — Python-Markdown md_in_html 扩展文档
- GitHub Markup — GitHub 的 Markdown 渲染说明
- OWASP XSS Prevention Cheat Sheet — XSS 防护参考