目次生成はWordPressの標準機能としては存在せず、プラグインで解決するか、テーマに実装するかの2択になります。
当テーマDAWNYでは、目次を自動生成する機能を標準搭載していますので、テーマファイルを編集して目次を自動生成する仕組みや仕様について解説します。
WordPressで目次を自動生成する方法
投稿ページでh2見出しが2つ以上ある場合に目次を自動生成するコードとして、DAWNYでは以下を採用しています。
function generate_toc($content) {
if (is_single()) {
$toc = '<div class="toc-container"><div class="toc-header">INFORMATION</div><div class="toc-content"><ul>';
// h2タグを目次の対象とする
$pattern = '/<h2[^>]*>(.*?)<\/h2>/';
// h2タグが何個あるかカウント
$h2_count = preg_match_all($pattern, $content, $h2_matches);
if ($h2_count >= 2 && preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$title = $match[1];
$slug = sanitize_title($title);
// h2タグに基づいた目次の項目を生成
$toc .= '<li><span class="toc-text"><a href="#' . $slug . '">' . esc_html($title) . '</a></span></li>';
// h2タグにIDを追加して目次からリンクできるようにする
$content = str_replace($match[0], '<h2 id="' . $slug . '">' . $title . '</h2>', $content);
}
$toc .= '</ul></div></div>';
// 目次を最初のh2タグの前に挿入
$firstHeaderFound = preg_match('/<h2[^>]*>/', $content, $firstMatch);
if ($firstHeaderFound) {
$content = preg_replace('/<h2[^>]*>/', $toc . '$0', $content, 1);
} else {
$content = $toc . $content; // 見出しがない場合は記事の最初に目次を追加
}
}
}
return $content;
}
add_filter('the_content', 'generate_toc');
DAWNYではtoc.phpという目次専用のファイルを作成して読み込んでいますが、function.phpに直接書いてしまっても構いません。お好みです。
※上記のコードはおそらくどんなテーマでも動くと思いますが、そのテーマ独自の関数やCSSなどと競合する可能性はありますので、もちろん動作保証はしません。
1. ページ内にあるh2見出しを取得する
まずはページ内に存在するh2見出しをすべて取得します。
// h2タグを目次の対象とする
$pattern = '/<h2[^>]*>(.*?)<\/h2>/';
// h2タグが何個あるかカウント
$h2_count = preg_match_all($pattern, $content, $h2_matches);
if ($h2_count >= 2 && preg_match_all($pattern, $content, $matches, PREG_SET_ORDER))
変数の定義も含んでいますが、この辺りのコードまででおこなっています。
正規表現でhtml内を検索して<h2></h2>で囲われているテキストを拾ってくるような仕組みにしています。
フッターやグローバルメニューなどにh2があると拾ってしまう可能性がありますが、DAWNYではメインコンテンツ以外にhタグを使わない方針ですので、そこのハンドリングはしていません。
必要に応じて読み取る範囲を制限するなどのカスタマイズするのが良いかと思います。
2. h2が2つ以上ある場合は目次項目を生成する
実際にカウントした結果、h2が2つ以上あったら目次を作ります。
foreach ($matches as $match) {
$title = $match[1];
$slug = sanitize_title($title);
// h2タグに基づいた目次の項目を生成
$toc .= '<li><span class="toc-text"><a href="#' . $slug . '">' . esc_html($title) . '</a></span></li>';
// h2タグにIDを追加して目次からリンクできるようにする
$content = str_replace($match[0], '<h2 id="' . $slug . '">' . $title . '</h2>', $content);
}
$toc .= '</ul></div></div>';
このあたりまでですね。
h2タグに、そのh2テキストをidとして付与しています。ここは実装方法によっては連番を振る場合などもあるのですが、#1 #2とかにしてしまうと誤動作の元になるので、普通にテキストを振っておくのがいいのではないかなと思っています。
h2タグのテキストを<li></li>でくくって、リンクとしてidで付与したテキストを当てることで、目次をタップすればその見出しまでページ内ジャンプする仕組みにしています。
このコードでは、最終的には以下のようなhtmlが出力されます。
<div class="toc-container">
<div class="toc-header">INFORMATION</div>
<div class="toc-content">
<ul>
<li><span class="toc-text"><a href="#目次1">目次1</a></span></li>
<li><span class="toc-text"><a href="#目次2">目次2</a></span></li>
<li><span class="toc-text"><a href="#目次3">目次3</a></span></li>
</ul>
</div>
</div>
</div>
課題として、ページ内にまったく同じ名称のh2タグが複数あった場合に誤動作が起きますが、普通に記事を書いていたらそんなことは起こらないだろうと思ってそのハンドリングはしていません。
3. 目次を最初のh2タグの前に挿入する
最後に、最初のh2タグの前に目次を挿入することで記事の体裁を仕上げています。
// 目次を最初のh2タグの前に挿入
$firstHeaderFound = preg_match('/<h2[^>]*>/', $content, $firstMatch);
if ($firstHeaderFound) {
$content = preg_replace('/<h2[^>]*>/', $toc . '$0', $content, 1);
} else {
$content = $toc . $content; // 見出しがない場合は記事の最初に目次を追加
}
これもまた正規表現でh2タグを検索して、最初に引っかかったタグの直前に目次のコードを差し込むような実装になっています。
※ちなみにこの実装を応用することで、たとえば3つ目のh2見出しの前に一律で広告バナーを差し込むとか、アフィリエイトブログでよくあるような運用も可能になります。
ちなみに基本的には起こり得ないのですが、h2タグが見つからない場合は一応記事の最初に追加するようにしています。
4 CSSやJavascriptでデザインを調整する
最後に、目次のデザインをCSSやJavascriptで整えたら完成です。
目次をカスタマイズできると便利
これは考え方や好みにもよるのですが、h2タグだけを目次にする場合もあれば、h3まで含める場合、h4まで含める場合など様々です。またh2が2つあれば目次を出力することも、3つ以上ないと目次を出さない場合もあります。
そのWebサイトの記事の内容や読者層、デザインによっても変わってきますので、自分で自由にカスタマイズしながら実装するのが一番です。
今後カスタマイズ方法も追記予定ですので、お楽しみに。