[2023-08-28 更新]
HTML 仕様では、HTML マークアップから目次(アウトライン)を作るルールが決められています。それをアウトラインアルゴリズムと呼びます。以前は section 要素などのセクショニング要素、h1 ~ h6 要素や hgroup 要素といった見出し要素を駆使してアウトラインが形成されましたが、実は、そのアルゴリズムは 2022 年 7 月に大幅に変更されました。ここでは、すでにネット上でも語りつくされている旧来のアルゴリズムと比較しながら最新のルールを詳しく解説していきます。
見出しレベル
アウトラインを理解するにあたり、まずは見出しレベルを理解する必要があります。見出しレベルとは、目次の階層の深さのことです。通常、ウェブページはページタイトルを 1 つ持ちます。これが最上位のレベルです。その下にツリー上に見出しがぶら下がる形になります。書籍に例えれば、本のタイトルの下に第一章、第二章、といった具合に見出しがぶら下がります。
ページタイトル
見出し1
見出し1.1
見出し1.2
見出し2
ご存じの通り、この見出しは h1 ~ h6 要素 でマークアップします。そして、HTML 仕様上の見出しレベルとは、この h1 ~ h6 要素の数字を指します。つまり 1 ~ 6 のことです。見出しレベルの値が小さいほど、見出しの階層は上位になります。
前述の目次を元にページをマークアップすると次のようになるでしょう。少しだけコンテンツも加えてあります。
<body>
<hgroup>
<h1>ページタイトル</h1>
<p>サブタイトル</p>
</hgroup>
<p>あいうえお。</p>
<h2>見出し1</h2>
<p>かきくけこ。</p>
<h3>見出し1.1</h3>
<p>さしすせそ。</p>
<h3>見出し1.2</h3>
<p>たちつてと。</p>
<h2>見出し2</h2>
<p>なにぬねの。</p>
</body>
目次の階層と HTML マークアップの階層が一致せず、非常に読みづらいことがお分かりいただけるでしょう。
セクションとは
HTML を学んでいる人にセクションを作り出す要素とは何かと聞けば、article 要素、section 要素、aside 要素、nav 要素のことだと答える人が多いでしょう。間違いではありませんが、それがすべてではありません。h1 ~ h6 要素からもセクションが作られます。
前述のマークアップのコンテンツは次のようなセクションに分けられていると解釈できます。
まず body
要素で一つのセクションが作られます。この body
要素のセクションの見出しは、その中で見出しレベルが小さい要素、つまり h1
要素の「ページタイトル」です。以降、h2
などの見出し要素が現れるたびに、新たなセクションが作られていきます。
アウトライン形成の流れ
前述のマークアップのアウトラインが形成される過程をもう少し詳しく見ていきましょう。
1. ページタイトル
1.1 見出し1
1.1.1 見出し1.1
1.1.2 見出し1.2
1.2 見出し2
この例では hgroup
要素がマークアップされていますが、実は、アウトライン形成においてこの要素の存在はまったく意味を持ちません。純粋に h1
~ h6
要素の存在のみでアウトラインを作っていくことになります。そして、マークアップを上から順に読んでいき、目次を付け足していきます。
まず最初に出くわすのが <h1>ページタイトル</h1>
です。そのため、まずアウトラインに「ページタイトル」が加わります。さらに見出しの数字が 1 ですので、最上位になります。
ページタイトル
次に <h2>見出し1</h2>
に出くわします。見出しの数字が 2 ですので、2 階層目の見出しとしてアウトラインに追加されます。
ページタイトル
見出し1
さらに HTML を下がっていくと <h3>見出し1.1</h3>
に出くわします。見出しの数字が 3 ですので、3 階層目の見出しとしてアウトラインに追加されます。
ページタイトル
見出し1
見出し1.1
このように、HTML を上から順に走査し、見出し要素に出くわすたびにアウトラインに追加されていきます。
section 要素は無くても良い?
前述の通り、h1
~ h6
要素の存在のみでアウトラインが作られました。では、section
要素などは不要なのでしょうか?いえ、そんなことはありません。次のマークアップをご覧ください。
<body>
<h1>商品一覧</h1>
<div>
<p>今日だけの特価セール!</p>
<h2>Apoh iPon 13</h2>
<p>…</p>
</div>
<div>
<p>残りわずか!</p>
<h2>SONNY Xepia 5 III</h2>
<p>…</p>
</div>
</body>
このマークアップのアウトラインは次の通り期待通りになります。
商品一覧
Apoh iPon 13
SONNY Xepia 5 III
ところが、セクションの区切り位置が機械的に読み取れません。もし見出し要素に遭遇したときをセクションの開始と仮定すると、おかしな位置でセクションが分離されてしまいます。以下は強引に分離したときのイメージです。
もちろん、もう少しマシな分離アルゴリズムを考えられないわけではないと思いますが、やはりあらゆるマークアップを網羅するようなアルゴリズムを作るのは難しいと言えます。
上記のマークアップのうち div
要素を section
要素に置き換えれば、確実に期待通りの位置にセクションの開始を持ってくることができます。
<body>
<h1>商品一覧</h1>
<section>
<p>今日だけの特価セール!</p>
<h2>Apoh iPon 13</h2>
<p>…</p>
</section>
<section>
<p>残りわずか!</p>
<h2>SONNY Xepia 5 III</h2>
<p>…</p>
</section>
</body>
このように section
要素などのセクショニング要素によって、セクションの区切りを明確にします。しかし、セクショニング要素はアウトライン形成には何ら影響を与えない点には注意してください。見出し要素がマークアップされていないセクショニング要素は、アウトラインに現れることはありません。
次の例はセクショニング要素の 1 つである nav
要素がマークアップされていますが、その中に見出し要素はありません。
<body>
<h1>商品一覧</h1>
<nav>
<a href="#">Apoh iPon 13</a> |
<a href="#">SONNY Xepia 5 III</a>
</nav>
<section>
<h2>Apoh iPon 13</h2>
</section>
<section>
<h2>SONNY Xepia 5 III</h2>
</section>
</body>
このマークアップから生成されるアウトラインは次のようになります。
商品一覧
Apoh iPon 13
SONNY Xepia 5 III
このように、見出し要素がない以上、たとえセクショニング要素がマークアップされていたとしても、アウトライン生成においては無視されてしまいます。
見出しレベルの誤用に注意
見出し要素の数字は、見出しの階層に合わせて使う必要があります。つまり、HTML を上から順に眺めていったとき、最初に遭遇する見出し要素は h1
要素であるべきです。一般的に最上位の見出しはページに 1 つですので、次に遭遇する見出し要素は h2
要素であるべきです。
以降、その次に遭遇する見出し要素は、直前の見出し要素のレベルより小さい、または、同じ、または 1 つだけ大きいレベルの見出し要素であるべきです。たとえば、h3
要素に遭遇したら、その次に遭遇する見出し要素は同じレベルの h3
要素か、一階層だけ移動した h2
要素または h4
要素ということになります。h4
要素に遭遇したら、その次に遭遇する見出し要素は h2
, h3
, h4
, h5
のいずれかです。
このように、見出しレベルは小さくなる分には h4
から h2
といった具合に数字が飛んでも構いませんが、レベルが大きくなる場合は必ず 1 つだけ増えます。
しかし、現実にはこのルールに逸脱したマークアップをしたことがあるのではないでしょうか。次のようなマークアップをしたとしましょう。
<body>
<h2>商品一覧</h2>
<section>
<h2>Apoh iPon 13</h2>
</section>
<section>
<h2>SONNY Xepia 5 III</h2>
</section>
</body>
このページの見出しは階層に関わらずすべて h2
要素でマークアップされています。人が見ればアウトラインの意図は理解できるものの、HTML 仕様のルール上は次のようなアウトラインが形成されてしまいます。
- - 商品一覧
- Apoh iPon 13
- SONNY Xepia 5 III
もう一つ例を見てみましょう。次の例は h1
要素と h2
要素が現れる順番が逆の場合です。
<body>
<h2>商品一覧</h2>
<section>
<h1>Apoh iPon 13</h1>
</section>
</body>
このマークアップは、次のようなアウトラインを形成します。
- - 商品一覧
- Apoh iPon 13
このように見出しレベルが飛んでしまうようなマークアップは、文章構造としては明らかに間違っていますので注意してください。
かつてのアウトラインアルゴリズム
2022 年 7 月以前の HTML 仕様では、前述とは全く異なるアウトラインアルゴリズムが規定されていました。現在は h1
~ h6
要素の存在のみでアウトラインが形成されますが、かつてのアウトラインアルゴリズムでは section
要素などのセクショニング要素でもアウトラインが形成される仕様でした。さらに、セクショニング要素のマークアップ階層だけで見出しの階層が決定できたため、セクショニング要素を使う限りにおいては見出し要素はすべて h1
要素だけでも問題ありませんでした(当時でも推奨はされていませんでした)。つまり次のようなマークアップが許されていました。
<body>
<h1>商品一覧</h2>
<section>
<h1>Apoh iPon 13</h1>
</section>
<section>
<h1>SONNY Xepia 5 III</h1>
</section>
</body>
そして、アウトライン形成から除外される要素というものが規定されていました。当時それは「セクショニングルート」と呼ばれていました。セクショニングルートと規定された要素は次の通りでした。
blockquote
body
details
dialog
fieldset
figure
td
名前の通り、セクショニングルートとはセクションの根っこを意味します。body
要素以外のセクショニングルートの中に見出し要素が存在した場合、body
要素のドキュメントのアウトラインからは除外されるというルールがありました。
たとえば、blockquote
要素の中に見出し要素が存在した場合、それはあくまでも引用なので、body
要素のドキュメントのアウトラインから除外されるのは理にかなっています。また、td
要素のコンテンツが一つのセクションとしてアウトラインに登場するのもおかしな話ですので、これも理にかなっていると言えます。
しかし、現在はそのようなルールはありませんので、blockquote
要素や td
要素の中に見出し要素があれば、それはアウトラインに現れることになります。その点、気を付けないといけませんね。
WordPress の自動目次生成
このブログは WordPress を使っていますが、各記事の冒頭に表示された目次を毎度人力で用意するのは大変です。当ブログでは、この記事を執筆時点では RTOC(Rich Table of Contents)というブラグインを使っており、自動的に目次が生成され記事の冒頭に挿入されます。RTOC に限らず、”Table of Contents” で検索すれば、数多くの WordPress プラグインが見つかります。
これらのプラグインが HTML 仕様のアウトライン生成アルゴリズムに基づいているのかは分かりませんが、とても分かりやすいアウトライン生成アルゴリズムの利用シーンなのではないでしょうか。
まとめ
現在の HTML 仕様のアウトライン生成ルールが、article 要素、section 要素、aside 要素、nav 要素といったセクショニング要素に全く依存していない点は意外だったのではないでしょうか。h1 ~ h6 要素だけでアウトラインが生成されるというのは、ある意味、HTML4 時代に先祖返りした感じがしますね。
ただし、あまりアウトラインにこだわりすぎる必要はありません。このブログも理想的なアウトラインどころか、かなりヘンテコなアウトラインが作られています。そんなブログの運営者である筆者が言っても説得力はありませんが、階層の上下関係がおかしくない程度であれば良いのではないでしょうか。
今回は以上で終わりです。最後まで読んでくださりありがとうございました。それでは次回の記事までごきげんよう。