WordPressでプラグインを使わずに目次を自動生成してみた

当サイトはアフィリエイト広告を利用しています。記事内にプロモーションが含まれる場合があります。

記事を書くだけで勝手に目次が付けばいいなと思い、やってみた。

免責

結構前に作っていたんだけど、今回修正が必要だったからChatGPTにうまいこと作り直してもらった。

私はエンジニアじゃないので、ChatGPTが返してきた各処理が何をやっているのかもそんなに把握していないが、表画面が望んでいる結果になればいいやの精神で使っている。(なんどかトライアンドエラーはしたが出来てない部分をChatGPTに伝えて作り直させただけ)

そんなでも参考になるかもって人は自分の責任で勝手に見て使えそうなら使ってください。

作るもの

求めた仕様は以下

  • 目次には記事本文内の全ての見出しを出す
  • 目次内の各見出しは本文内への該当箇所へのページ内リンクにする
  • たまにある見出しがほとんどない記事では目次を出さないようにする(見出し2個以上で表示)
  • 目次は本文内最初の見出しの手前に表示させる(最初の見出しより前は導入文扱いにしたい)
  • h2の下にh3、というように正しく(?)運用しているはずだから、その階層がわかるようにする

作ってみた

出来上がった目次

このページの目次も今回のが自動で作ってるんだけど、h3までしか使ってないし、数もすくないから、見出しが多い記事を参考に貼っておく。出来上がったものを使うとこんな感じになります。

実際のコード

でこれを実際に作らせたのがこちら
single.phpのような対応するテンプレートの、本文を出力している箇所をこれにすればでるはず。

<?php
          // 自動目次生成プログラム

          // カウント初期化
          $counts = [2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0];

          // コンテンツ取得&フィルタ
          $content = apply_filters('the_content', get_the_content());
          $content = str_replace(']]>', ']]&gt;', $content);

          // h2〜h6 の見出しをすべて抽出
          preg_match_all('@<h([2-6])\b[^>]*>.*?</h\1>@i', $content, $matches);
          $headings = $matches[0];

          if (count($headings) > 1) {
            // 目次HTMLの開始タグ
            $toc  = '<ul class="table-of-contents">';
            $toc .= '<p class="table-of-contents__heading">目次</p>';

            foreach ($headings as $heading) {
              // レベルとテキスト部分を分解
              preg_match('@<h([2-6])\b[^>]*>(.*?)</h\1>@i', $heading, $m);
              $level = (int) $m[1];
              $text  = $m[2];

              // カウント増加&下位レベルリセット
              foreach ($counts as $lvl => $_) {
                if ($lvl === $level) {
                  $counts[$lvl]++;
                } elseif ($lvl > $level) {
                  $counts[$lvl] = 0;
                }
              }

              // ID用パーツを抽出
              $parts = [];
              for ($i = 2; $i <= $level; $i++) {
                if ($counts[$i] > 0) {
                  $parts[] = $counts[$i];
                }
              }
              $id = 'content' . implode('-', $parts);

              // 新しい見出しタグを生成(ID追記)
              $new_heading = preg_replace(
                '@<h' . $level . '\b([^>]*)>@i',
                '<h' . $level . '$1 id="' . $id . '">',
                $heading
              );

              // 元の見出しを差し替え
              $content = str_replace($heading, $new_heading, $content);

              // 目次に追加
              $toc .= '<li class="table-of-contents__h' . $level . '">'
                . '<a href="#' . $id . '">'
                . '<span>' . implode('-', $parts) . '.</span>'
                . strip_tags($text)
                . '</a>'
                . '</li>';
            }

            // 目次HTMLの閉じタグ
            $toc .= '</ul>';

            // 最初の h2〜h6 の前に目次を挿入
            $content = preg_replace(
              '@(<h[2-6]\b[^>]*>)@i',
              $toc . '$1',
              $content,
              1
            );
          }

          // 出力
          echo $content;
          ?>

結論

もうプログラムのちょっとした修正をやってみた類の記事は全部この結論になる気がするけど、

AIがすげぇ

スポンサーリンク

スポンサーリンク

テストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキスト

テストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキストテストテキスト