Markdown 與 HTML 混合使用完全指南

Markdown 寫起來很舒服,但總會碰到它搞不定的排版需求——比如讓圖片置中、設定文字顏色、做個可摺疊的內容區域,或者用 HTML 表格實現跨行合併。這時候你可能會搜尋 "markdown in html" 或 "html in markdown",想知道這兩種格式能不能混著用。

答案是肯定的。Markdown 的設計者 John Gruber 在最初設計時就明確說過:HTML 是 Markdown 的發布格式,Markdown 不是 HTML 的替代品,而是它的補充。所以在 Markdown 檔案裡直接寫 HTML 標籤,不是什麼 hack,而是被官方鼓勵的做法。

不過這裡面有不少細節值得注意。比如,行內標籤和區塊級標籤的行為完全不同;有些平台支援 markdown="1" 屬性讓 HTML 標籤內部的 Markdown 語法也能生效,但有些平台完全忽略它。我在這篇文章裡會把這些規則和差異講清楚,並分享一些實際使用中的經驗。

Markdown HTML 混合使用示意圖

核心規則:行內標籤 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 的圖片語法 ![alt](url) 沒有指定尺寸和對齊方式的選項。如果你想控制圖片的顯示大小或者讓它置中,就得用 HTML。

<!-- 設定圖片寬度 -->
<img src="image.png" width="300" alt="範例圖片">

<!-- 圖片置中 -->
<div align="center">
<img src="image.png" width="80%" alt="置中圖片">
</div>

在 GitHub 上,<img> 標籤的 widthheight 屬性是支援得比較好的。不過 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 的支援程度差異很大。我根據實際測試和官方文件整理了下面的對比表。

功能GitHubGitLabJekyll/KramdownPython-MarkdownObsidianTypora
行內 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">

![專案 Logo](logo.png)

**一個簡潔的專案描述**

[![GitHub Stars](https://img.shields.io/github/stars/user/repo)](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(比如 positionz-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>

大多數平台會過濾掉 onerroronclick 這類事件屬性和 javascript: 協定的連結。但如果你自己在搭建 Markdown 渲染系統,需要確保使用 XSS 過濾庫(比如 DOMPurify)來清理 HTML 內容。

內容安全策略

如果你在用 Markdown 解析庫(如 marked.js、markdown-it)構建自己的應用,建議:

  1. 始終啟用 HTML sanitization(HTML 清理)選項
  2. 使用白名單而非黑名單來過濾 HTML 標籤和屬性
  3. 不要信任使用者提交的 Markdown 中的 HTML 內容

來源:OWASP XSS Prevention Cheat Sheet

總結

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 來補充。兩者配合使用,才是最實用的方式。

參考資料