WordPressで文中に目次を自動生成する方法|プラグイン不要で実装可能

この記事では、WordPressの自動で目次(Table of Contents)を生成する方法を解説します。クラシックエディタでもそのまま貼り付けて使えるように、PHPやJavaScript、CSSも含めたサンプルです。

1 実装のポイント

  • 記事の見出し(h2・h3)を自動で検出して目次を生成
  • ショートコードやプラグインを使わず、functions.phpとJavaScriptで実装可能
  • CSSでスタイルを調整し、テーマごとに見た目を最適化

2 functions.php にコードを追加


// ------------------------------
// 記事本文に階層付き番号+折りたたみボタン付き目次を挿入
// ------------------------------
function insert_collapsible_numbered_toc($content) {
    if (is_singular() && in_the_loop() && is_main_query()) {

        // ショートコード [no-toc] があれば目次を生成しない
        if (has_shortcode($content, 'no-toc')) {
            return $content;
        }

        preg_match_all('/<h([2-3])>(.*?)<\/h[2-3]>/i', $content, $matches, PREG_SET_ORDER);
        if (!empty($matches)) {
            $toc = '<div class="toc-container">';
            $toc .= '<div class="mokuji-wrapper">';
            $toc .= '<span class="mokuji"><strong>目次</strong></span>';
            $toc .= '<button type="button" class="toc-toggle">[非表示]</button>';
            $toc .= '</div>';
            $toc .= '<ul style="clear:both;">';
            $h2_count = 0;
            $h3_count = 0;
            foreach ($matches as $match) {
                $level = $match[1];
                $text = strip_tags($match[2]);
                $anchor = sanitize_title($text);
                if ($level == 2) { $h2_count++; $h3_count = 0; $num = $h2_count; }
                elseif ($level == 3) { $h3_count++; $num = $h2_count . '.' . $h3_count; }
                $toc .= '<li class="toc-level-' . $level . '"><a href="#' . $anchor . '">' . $num . ' ' . $text . '</a></li>';
                $content = str_replace($match[0], '<h' . $level . ' id="' . $anchor . '">' . $num . ' ' . $text . '</h' . $level . '>', $content);
            }
            $toc .= '</ul></div>';
            $content = $toc . $content;
        }
    }
    return $content;
}
add_filter('the_content', 'insert_collapsible_numbered_toc');

// 目次折りたたみスクリプトを読み込む
function enqueue_toc_script() {
    wp_enqueue_script(
        'auto-toc',
        get_stylesheet_directory_uri() . '/js/auto-toc.js',
        array(), // 依存なし
        '1.0',
        true // フッターで読み込む
    );
}
add_action('wp_enqueue_scripts', 'enqueue_toc_script');

// [no-toc] ショートコード(目次非表示用)
function shortcode_no_toc($atts, $content = null) {
    return ''; // 実際には何も表示せず、コメントとして残す
}
add_shortcode('no-toc', 'shortcode_no_toc');
補足:

  • 見出しが存在する投稿でのみ目次が自動挿入されます。
  • 目次の順序は本文の見出し順に従います。
  • h2 と h3 が対象。必要に応じて h4 なども対象に追加可能です。

3 目次を非表示にしたい場合

特定のページや投稿で目次を表示したくない場合は、本文中にショートコード [no-toc] を挿入してください。このショートコードを入れると、そのページでは自動生成される目次は表示されません。

  • ショートコードは本文のどこに入れても構いません(先頭でも最後でもOK)
  • ショートコード自体は何も表示されず、コメントとして残るだけです

記事内で表示する場合:


[no-toc]
  

4 javascript にコードを追加

ファイル名:auto-toc.js
保存先:wp-content/themes/child-theme/js/auto-toc.js


document.addEventListener('DOMContentLoaded', function() {
    // 目次折りたたみボタン
    const toggleBtn = document.querySelector('.toc-container .toc-toggle');
    const tocList = document.querySelector('.toc-container ul');

    if (toggleBtn && tocList) {
        toggleBtn.addEventListener('click', function() {
            // 現在の表示状態を取得
            const isHidden = window.getComputedStyle(tocList).display === 'none';

            if (isHidden) {
                tocList.style.display = 'block';
                toggleBtn.textContent = '[非表示]';
            } else {
                tocList.style.display = 'none';
                toggleBtn.textContent = '[表示]';
            }
        });
    }

    // スムーズスクロール
    const header = document.querySelector('.site-header') || document.querySelector('header'); // 固定ヘッダー
    const headerHeight = header ? header.offsetHeight : 0;

    document.querySelectorAll('.toc-container a').forEach(function(anchor) {
        anchor.addEventListener('click', function(e) {
            e.preventDefault(); // デフォルトジャンプを防ぐ
            const target = document.querySelector(this.getAttribute('href'));
            if(target){
                const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - headerHeight - 10; 
                window.scrollTo({
                    top: targetPosition,
                    behavior: 'smooth'
                });
            }
        });
    });
});

4.1 ディレクトリ構造

  • child-theme/
    • style.css
    • functions.php
    • footer.php
    • header.php
    • js/
      • auto-toc.js
    • images/
      • logo.png

5 汎用CSS


/* -------------------------
*  投稿本文に目次を挿入するフィルター
---------------------------- */

/* 目次全体のコンテナ */
.toc-container {
    display: inline-block;       /* コンテンツ幅に合わせる */
    background: #edf6ff;        /* 背景色 */
    border: 1px solid #999;     /* 枠線 */
    padding: 10px;              /* 内側余白 */
    margin-bottom: 20px;        /* 下余白 */
    max-width: 100%;            /* 親幅を超えない */
    box-sizing: border-box;     /* paddingとborderを幅に含める */
    font-size: 80%;             /* 文字サイズを少し小さめ */
}

/* タイトル+非表示ボタンのラッパー */
.mokuji-wrapper {
    display: inline-flex;        /* 横並びにする */
    justify-content: center;     /* 横方向中央揃え */
    align-items: center;         /* 縦方向中央揃え */
    gap: 10px;                   /* タイトルとボタンの間隔 */
    margin-bottom: 10px;         /* 下余白 */
    width: 100%;                 /* 親幅いっぱいで中央揃え */
}

/* 「目次」タイトル */
.mokuji {
    font-weight: bold;           /* 太字 */
    font-size: 1.2em;            /* 少し大きめ */
}

/* 折りたたみボタン([非表示]) */
.toc-toggle {
    cursor: pointer;             /* マウスオーバーで指アイコン */
    color: #666;                 /* グレー文字 */
    text-decoration: none;       /* 下線なし */
    background: none;            /* 背景なし */
    border: none;                /* 枠なし */
    padding: 0;                  /* 余白なし */
    font-size: 1.1em;            /* 少し大きめ */
}

/* 目次リスト全体 */
.toc-container ul {
    display: none;               /* 目次リスト全体を初期は非表示 */
    list-style: none;            /* デフォルトリストマーカーを消す */
    padding-left: 0;             /* 左余白を削除 */
    margin: 0;                   /* 外側余白を削除 */
    line-height: 1.0;            /* 行間調整(必要に応じて) */
}

/* リスト項目 */
.toc-container li {
    margin-left: 0;              /* 左余白リセット */
    line-height: 1.5;            /* 行間を少し広めに */
}

/* 階層ごとのインデント */
.toc-level-2 {
    padding-left: 0;             /* h2 は左揃え */
}
.toc-level-3 {
    padding-left: 20px;          /* h3 は少し右にずらす */
}

/* リンクの基本スタイル */
.toc-container a {
    text-decoration: none;       /* 下線なし */
    color: #666;                 /* グレー文字 */
}

/* リンクのホバー時 */
.toc-container a:hover {
    text-decoration: underline;  /* 下線表示 */
    color: #444;                 /* 少し濃いグレー */
}
補足:

デフォルトは非表示になっています。
display: none;を消すと表示がデフォルトになります。
.toc-container ul {
  display: none; /* 目次リスト全体を初期は非表示 */
}

6 完成イメージ

左がTable of Contents Plusプラグインで作成されたもの
右がコードで自動作成されたもの
目次

7 代用プラグイン

  • Table of Contents Plus:同様に投稿本文に目次を自動生成
  • Easy Table of Contents:ショートコードで柔軟に目次を挿入可能

8 まとめ

  • functions.phpにコードを追加するだけで、投稿本文中に目次を自動生成可能
  • JavaScriptでスムーズスクロールも実装できる
  • CSSでテーマに合わせて目次のデザインを変更可能
  • 目次を表示したくない場合はショートコード [no-toc] を挿入すれば目次は非表示