長文沒有目錄,讀者就得一路往下捲,像在網頁裡玩「找不同」:不同的是重點到底藏在哪裡。Table of Contents(TOC,文章目錄) 能把章節標題變成頁內連結,讓讀者先看地圖,再決定要走哪條路。

這篇讀完,你會知道 TOC 對閱讀體驗與 SEO 能幫多少、如何用原生 HTML 做出穩定錨點,以及靜態網站、JavaScript 和 WordPress 各自適合什麼情境。也會看到本站用 [[toc]] 時,建置流程實際做了什麼。

TOC 到底解決什麼問題

先解決讀者的問題

一份好的 TOC 至少有三個用途:

  • 讓讀者快速掃過文章範圍,判斷內容是否值得讀
  • 讓讀者直接跳到需要的章節,少滑幾個螢幕
  • 讓讀者複製頁面網址後面的 #錨點,把特定段落分享給別人

這些是使用者體驗上的好處,不是「放上 TOC,排名立刻升天」的魔法。SEO 沒有魔法按鈕,不然大家早就把鍵盤換成水晶球了。

SEO 能幫多少:務實版

Google 並沒有保證「加入 TOC 就會排名更高」。比較可靠的做法,是把 TOC 做成一般 HTML 連結,並讓文章有清楚的標題結構:

  • 用一個主要標題,再用 H2、H3 分出章節與小節
  • 使用真正的 <a href="#section-id">,而不是只有 JavaScript 點擊事件
  • 讓連結文字描述目標內容,不要全部都叫「點這裡」

Google 的文件指出,通常需要有 href<a> 元素,搜尋系統才容易擷取連結;至於搜尋結果是否顯示頁內跳轉連結,仍由搜尋系統自動判斷,不能手動保證。

另外,別把頁內跳轉(jump link)和網站導覽列裡的 sitelinks 混在一起。Google 已宣布從 2024 年 11 月 21 日起移除搜尋結果中的 sitelinks search box 視覺元素,但這不等於一般 sitelinks 或頁內錨點消失。

先把標題和錨點做好

TOC 只是導航,不會替混亂的內容結構打掃房間。實作前先確認:

  1. 一篇文章有一個主要 H1;本站的版型會處理文章標題。
  2. 大章節用 H2,小節用 H3,層級不要從 H2 突然跳到 H4。
  3. 每個目標標題都有唯一且穩定的 id
  4. TOC 的 href 必須和目標標題的 id 完全相同。

最小 HTML 例子如下:

<article>
	<h1>文章標題</h1>
	<nav aria-label="文章目錄">
		<ol>
			<li><a href="#intro">簡介</a></li>
			<li><a href="#setup">開始實作</a></li>
		</ol>
	</nav>
	<section id="intro">
		<h2>簡介</h2>
		<p>這裡是文章簡介。</p>
	</section>
	<section id="setup">
		<h2>開始實作</h2>
		<p>這裡是實作內容。</p>
	</section>
</article>

id 不能在同一頁重複。若兩個標題都叫「結語」,產生器就需要處理碰撞,例如輸出 結語結語-1。手動指定 ID 時也一樣:改了標題後,要一起檢查既有連結,否則讀者只會被送到一個很有禮貌的 404——它什麼都沒做,但態度很好。

三種 TOC 實作方式

A. 建置時產生靜態 TOC

靜態網站最適合這種方式。Markdown 內文放一個標記:

[[toc]]

建置工具讀取標題,輸出一般 HTML 清單與錨點。這種做法不需要額外的前端 JavaScript,HTML 載入時就有內容,通常也比較容易處理無 JavaScript、無障礙和版面穩定性。

本站的 [[toc]]markdown-it-table-of-contents 處理,而且目前設定只列出 H2。換句話說,這篇文章的 H3 會出現在正文裡,但不會自動列進本站的目錄;這是目前建置設定的結果,不是 Markdown 的宇宙定律。

B. 用 JavaScript 動態掃描標題

如果頁面內容是執行後才出現,或需要摺疊目錄、標示目前章節,JavaScript 就很有用。不過它最好是「增強功能」,不要成為唯一的導航來源。

下面是一個不使用 innerHTML 的簡化範例。使用 textContentcreateElement,可以避免把標題文字直接當成 HTML 解析:

<nav id="table-of-contents" aria-label="文章目錄">
	<h2>目錄</h2>
	<ol id="toc-list"></ol>
</nav>
const tocList = document.querySelector('#toc-list');
const headings = document.querySelectorAll('article h2');
const usedIds = new Set();

headings.forEach((heading, index) => {
	let id = heading.id || `section-${index + 1}`;
	let suffix = 1;

	while (usedIds.has(id)) {
		id = `${heading.id || `section-${index + 1}`}-${suffix}`;
		suffix += 1;
	}

	heading.id = id;
	usedIds.add(id);

	const item = document.createElement('li');
	const link = document.createElement('a');
	link.href = `#${id}`;
	link.textContent = heading.textContent;
	item.append(link);
	tocList.append(item);
});

這段程式的重點不是「看到 H2 就開心地生一堆 <li>」,而是確保每個連結都有 href,每個目標都有唯一 ID。若頁面內容在這段程式執行後才載入,就需要把建立目錄的時機放到內容完成後;不要只在 DOMContentLoaded 時猜測內容已經全部出現。

C. 使用 WordPress 外掛或區塊

WordPress 可以使用 TOC 外掛,或由佈景主題/編輯器提供目錄區塊。選擇時先看這些條件:

  • 能否排除不想出現的標題,例如「參考資料」
  • 輸出的連結是否是真正的 <a href="#...">
  • 是否能處理重複標題並產生唯一 ID
  • 手機版是否容易操作,且不會載入不必要的腳本與樣式

外掛功能越多不代表越適合。若只需要一份簡單目錄,卻順便載入一套動畫、三個 CSS 檔和一位不知道在做什麼的 JavaScript,這就有點像為了煮咖啡買了一台工業鍋爐。

一個可直接測試的完整範例

你也可以開啟 CSS Counter 列表示範頁,查看實際的列表編號效果。下面則是一個不依賴框架的最小頁面:

<!doctype html>
<html lang="zh-Hant">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>TOC 示範</title>
</head>
<body>
	<article>
		<h1>TOC 示範文章</h1>
		<nav aria-label="文章目錄">
			<h2>目錄</h2>
			<ol>
				<li><a href="#intro">簡介</a></li>
				<li><a href="#practice">實作</a></li>
			</ol>
		</nav>
		<section id="intro">
			<h2>簡介</h2>
			<p>TOC 先提供路線圖,讀者再決定要不要出發。</p>
		</section>
		<section id="practice">
			<h2>實作</h2>
			<p>點擊目錄連結,應該會跳到這個段落。</p>
		</section>
	</article>
</body>
</html>

2026 年實務檢查清單

發布前可以逐項確認:

  • [ ] H1、H2、H3 層級符合內容結構,沒有為了字體大小亂用標題
  • [ ] 每個 TOC 連結都有 href="#...",目標標題有對應且唯一的 id
  • [ ] 目錄文字和目標標題一致,讀者不需要玩猜謎遊戲
  • [ ] 靜態網站優先在建置時輸出;JavaScript 只負責互動增強
  • [ ] 目錄容器有 navaria-label,讓輔助科技知道它是導航
  • [ ] 手機版容易收合或掃讀,不會比文章本身還有存在感
  • [ ] 已實際點過每個連結,也測過重新整理後的 #錨點

結構化資料可以協助搜尋系統理解頁面,但不能取代清楚的標題、內文和可操作的連結。最實際的 SEO 策略通常不華麗:先讓人找得到、讀得懂、跳得準,再期待搜尋系統看懂。

常見問題

TOC 一定要放在文章最前面嗎?

長文通常放在引言後最方便讀者掃描;如果是桌面版側欄,也可以固定在旁邊。重點是目錄要在 HTML 中真實存在,且連結能直接跳到內容。

中文標題的 id 應該怎麼產生?

可以使用可讀的中文 ID、拼音,或其他穩定的 slug。選哪一種不是重點,重點是產生規則要穩定、結果不能重複,而且 TOC 和標題必須使用同一套規則。本站允許中文 slug,並讓 TOC 與標題 ID 共用自訂 slugify。

要不要把 H3 也放進目錄?

看文章長度與目錄深度。小節很多時,H3 會讓目錄變成另一篇文章;小節較少時,列出 H3 則能幫助讀者定位。本站目前只輸出 H2,因此若需要 H3 目錄,必須先調整建置設定,再同步檢查樣式與行動版體驗。

TOC 會不會和其他外掛衝突?

可能會。最常見的問題是兩個工具同時替標題產生 ID,結果一個工具改完,另一個工具的連結就失效。建議由單一工具負責 ID,並在建置後檢查 HTML 中是否有重複 ID 或找不到的 href

結語

TOC 的核心價值很簡單:讓讀者更快找到重點,也讓頁面結構更容易被理解。靜態站通常優先選建置時產生的原生 HTML;需要高亮、摺疊或動態內容時,再讓 JavaScript 上場;WordPress 則要慎選外掛,別讓目錄為了五行功能背上一整個工具箱。

如果你正在走靜態網站路線,可以接著閱讀:11ty 入門PageSpeed 與 Core Web Vitals。文章審完、錨點點完,再來一杯咖啡——這次不必讓咖啡也建立一個 TOC。

參考資料