【WordPress】投稿本文内の見出しを自動で目次に変換する方法(プラグインなし)

【WordPress】投稿本文内の見出しを自動で目次に変換する方法(プラグインなし) WordPress

SEOやユーザビリティの観点から、記事内に目次(Table of Contents)を設置するのは非常に有効です。
多くのユーザーはプラグインを使って目次を生成していますが、**「できるだけ軽量にしたい」「プラグインは使いたくない」**という場合、PHPと正規表現を使って自前で目次を生成することが可能です。

この記事では、WordPressの投稿本文に含まれる見出し(

や など)を自動で目次に変換する方法を紹介します。 ステップ1:functions.php に目次生成関数を追加

function generate_table_of_contents($content) {
  if (is_single()) {
    $matches = [];
    $headings = [];

    // <h2>タグをすべて取得(必要なら h3 も追加)
    preg_match_all('/<h2.*?>(.*?)<\/h2>/', $content, $matches);

    if (!empty($matches[1])) {
      foreach ($matches[1] as $index => $heading) {
        $slug = 'toc-' . $index;
        $headings[] = '<li><a href="#' . $slug . '">' . esc_html(strip_tags($heading)) . '</a></li>';

        // ID付きのh2に置き換え
        $original = $matches[0][$index];
        $replacement = preg_replace('/<h2(.*?)>/', '<h2$1 id="' . esc_attr($slug) . '">', $original);
        $content = str_replace($original, $replacement, $content);
      }

      // 目次HTMLを生成
      $toc = '<div class="toc-box">';
      $toc .= '<p class="toc-title">目次</p>';
      $toc .= '<ul class="toc-list">' . implode('', $headings) . '</ul>';
      $toc .= '</div>';

      // 目次を本文の先頭に挿入
      $content = $toc . $content;
    }
  }

  return $content;
}
add_filter('the_content', 'generate_table_of_contents');

ステップ2:CSSで目次を装飾(任意)

.toc-box {
  background: #f8f8f8;
  padding: 1em;
  border-left: 4px solid #0073aa;
  margin-bottom: 1.5em;
}
.toc-title {
  font-weight: bold;
  margin-bottom: 0.5em;
}
.toc-list {
  list-style: none;
  padding-left: 1em;
}
.toc-list li {
  margin: 0.3em 0;
}
.toc-list a {
  text-decoration: none;
  color: #0073aa;
}

補足:対象見出しを変更したい場合

h2 → h3 に変更するには、正規表現の対象を変えるだけです:

preg_match_all('/<h3.*?>(.*?)<\/h3>/', $content, $matches);

複数見出し階層に対応させたい場合は、h2 と h3 両方を抽出し、ネスト構造を組むロジックに改良する必要があります。

複数見出し階層に対応させる方法(h2・h3対応)

記事内の構成によっては、h2 を大見出し、h3 をその下の小見出しとして使っているケースもあるでしょう。
そのような場合、目次も階層構造に対応させることで、より視認性の高い構成にすることが可能です。

以下のコードは、h2 と h3 を抽出し、入れ子構造の目次を作成します。

function generate_table_of_contents_with_hierarchy($content) {
  if (is_single()) {
    $pattern = '/<h([2-3])[^>]*>(.*?)<\/h[2-3]>/i';
    preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE);

    if (!empty($matches[0])) {
      $toc = '<div class="toc-box"><p class="toc-title">目次</p><ul class="toc-list">';
      $last_level = 2;
      $id_count = 0;

      foreach ($matches[0] as $index => $match) {
        $tag_level = intval($matches[1][$index][0]);
        $heading_text = strip_tags($matches[2][$index][0]);
        $id = 'toc-' . $id_count;
        $id_count++;

        // 見出しにIDを追加
        $original_tag = $match[0];
        $new_tag = preg_replace('/<h([2-3])(.*?)>/', '<h$1$2 id="' . esc_attr($id) . '">', $original_tag);
        $content = substr_replace($content, $new_tag, $match[1], strlen($original_tag));

        // 階層構造の制御
        if ($tag_level > $last_level) {
          $toc .= '<ul>';
        } elseif ($tag_level < $last_level) {
          $toc .= '</ul>';
        }

        $toc .= '<li><a href="#' . esc_attr($id) . '">' . esc_html($heading_text) . '</a></li>';
        $last_level = $tag_level;
      }

      // 終了タグを補完
      $toc .= str_repeat('</ul>', max(0, $last_level - 2));
      $toc .= '</ul></div>';

      // 目次を先頭に挿入
      $content = $toc . $content;
    }
  }

  return $content;
}
add_filter('the_content', 'generate_table_of_contents_with_hierarchy');

まとめ

プラグインを使わずに、WordPress投稿本文から見出しを自動抽出して目次を生成する方法を紹介しました。
シンプルながら軽量かつ柔軟で、自分好みにカスタマイズしたい方におすすめです。