MENU

【WordPress】開閉できる目次をプラグイン無しで自動生成する方法

WordPressで目次を自動生成する方法として、プラグインを使用する方法が一般的ですが、プラグイン無しでも簡単に実装することができます。

本記事では、プラグインを使用せずにPHPコードを追加して目次を自動生成する方法について、具体的な手順とその利点を詳しく解説します。

最終的にこのような目次が自動生成されるようになったら完成です。

目次から探す

目次生成の仕組み

WordPressで目次を自動生成するためには、ページ内の見出し要素を取得し、それらを基に目次を構築する必要があります。

ここでは、具体的な目次生成の仕組みについて詳しく解説します。

対象となる見出し

目次に含める見出し要素として、一般的にはh2h3が使用されます。

これらの見出し要素を取得し、目次に反映させる方法について説明します。

h2とh3の取得

まず、ページ内のh2h3要素を正規表現を用いて取得します。

PHPのpreg_match_all関数を使用することで、これらの見出し要素を効率的に抽出できます。

以下のコードは、h2h3要素を取得するための正規表現の例です。

$heading_pattern = '/<(h2|h3)(.*?)>(.*?)<\/(h2|h3)>/is';
$matches = array();
$heading_count = preg_match_all($heading_pattern, $content, $matches, PREG_SET_ORDER);

このコードでは、$contentにページのHTMLコンテンツが格納されており、$matchesに見出し要素が格納されます。

見出しの階層構造

取得した見出し要素は、階層構造を持つ必要があります。

h2はトップレベルの見出しとして扱い、h3はそのサブレベルの見出しとして扱います。

この階層構造を維持することで、目次が視覚的にわかりやすくなります。

アンカーポイントの設定

目次から各見出しにジャンプするためには、アンカーポイントを設定する必要があります。

これにより、ユーザーが目次のリンクをクリックすると、対応する見出しにスムーズに移動できます。

IDを持つspanの挿入

各見出し要素に一意のIDを付与するために、spanタグを挿入します。

以下のコードは、見出し要素にIDを付与する方法の例です。

$content = preg_replace_callback($heading_pattern, function ($match) use ($headings) {
    static $index = 0;
    $id = $headings[$index]['id'];
    $index++;
    return sprintf('<%1$s%2$s><span id="%3$s">%4$s</span></%1$s>', $match[1], $match[2], $id, $match[3]);
}, $content);

このコードでは、各見出し要素に対して一意のIDを持つspanタグを挿入しています。

アンカーリンクの生成

目次の各項目は、対応する見出し要素へのアンカーリンクとして機能します。

以下のコードは、アンカーリンクを生成する方法の例です。

$table_of_contents .= '<li class="toc__item toc__item--h2"><a href="#' . $heading['id'] . '" class="toc__link">' . $heading['text'] . '</a>';

このコードでは、<a>タグを使用してアンカーリンクを生成し、目次の項目として追加しています。

以上が、WordPressで目次を自動生成するための基本的な仕組みです。

次のセクションでは、具体的なPHPコードの解説に進みます。

PHPコードの解説

WordPressで目次を自動生成するためのPHPコードについて、具体的な解説を行います。

このセクションでは、コード全体の構成と各関数の詳細について説明します。

コード全体の構成

目次を自動生成するためのPHPコードは、主に2つの関数で構成されています。

これらの関数は、見出し要素を取得し、目次を生成し、ページのコンテンツに目次を挿入する役割を果たします。

generate_table_of_contents関数

この関数は、取得した見出し要素を基に目次のHTML構造を生成します。

具体的には、h2h3要素をリスト形式で目次に追加し、階層構造を維持します。

insert_table_of_contents関数

この関数は、ページのコンテンツに目次を挿入する役割を果たします。

具体的には、見出し要素を取得し、generate_table_of_contents関数を呼び出して目次を生成し、最初のh2要素の上に目次を挿入します。

各関数の詳細

それでは、各関数の詳細についてさらに掘り下げて解説します。

generate_table_of_contents関数

この関数は、見出し要素の配列を受け取り、目次のHTML構造を生成します。

以下のコードは、generate_table_of_contents関数の全体です。

function generate_table_of_contents($headings) {
    $table_of_contents = '<div class="toc__container"><p class="toc__title">目次</p><ol class="toc__list">';
    $current_h2_item = '';
    $has_h3 = false;
    foreach ($headings as $heading) {
        if ($heading['tag'] == 'h2') {
            if ($current_h2_item !== '') {
                if ($has_h3) {
                    $table_of_contents .= '</ol>';
                }
                $table_of_contents .= '</li>';
            }
            $current_h2_item = '<li class="toc__item toc__item--h2"><a href="#' . $heading['id'] . '" class="toc__link">' . $heading['text'] . '</a>';
            $has_h3 = false;
            $table_of_contents .= $current_h2_item;
        } else {
            if (!$has_h3) {
                $table_of_contents .= '<ol class="toc__sublist">';
                $has_h3 = true;
            }
            $table_of_contents .= '<li class="toc__item toc__item--h3"><a href="#' . $heading['id'] . '" class="toc__link">' . $heading['text'] . '</a></li>';
        }
    }
    if ($current_h2_item !== '') {
        if ($has_h3) {
            $table_of_contents .= '</ol>';
        }
        $table_of_contents .= '</li>';
    }
    $table_of_contents .= '</ol></div>';
    return $table_of_contents;
}
目次のHTML構造

この関数では、目次のHTML構造を生成します。

目次は<div>タグで囲まれ、<ol>タグを使用してリスト形式で表示されます。

h2要素はトップレベルのリスト項目として追加され、h3要素はサブリストとして追加されます。

h2とh3の処理

h2要素が見つかった場合、現在のh2項目が空でない場合は、サブリストを閉じてからリスト項目を閉じます。

その後、新しいh2項目を作成し、目次に追加します。

h3要素が見つかった場合、サブリストが存在しない場合は新しいサブリストを作成し、h3項目を追加します。

insert_table_of_contents関数

この関数は、ページのコンテンツに目次を挿入する役割を果たします。

以下のコードは、insert_table_of_contents関数の全体です。

function insert_table_of_contents($content) {
    if (is_single()) {
        $heading_pattern = '/<(h2|h3)(.*?)>(.*?)<\/(h2|h3)>/is';
        $matches = array();
        $heading_count = preg_match_all($heading_pattern, $content, $matches, PREG_SET_ORDER);
        if ($heading_count >= 2) {
            $headings = array();
            $heading_counter = 0;
            foreach ($matches as $match) {
                $headings[] = array(
                    'tag' => $match[1],
                    'attributes' => $match[2],
                    'text' => $match[3],
                    'id' => 'heading-' . $heading_counter
                );
                $heading_counter++;
            }
            $content = preg_replace_callback($heading_pattern, function ($match) use ($headings) {
                static $index = 0;
                $id = $headings[$index]['id'];
                $index++;
                return sprintf('<%1$s%2$s><span id="%3$s">%4$s</span></%1$s>', $match[1], $match[2], $id, $match[3]);
            }, $content);
            $table_of_contents = generate_table_of_contents($headings);
            $first_h2_position = strpos($content, '<h2>');
            if ($first_h2_position !== false) {
                $content = substr_replace($content, $table_of_contents, $first_h2_position, 0);
            }
        }
    }
    return $content;
}
add_filter('the_content', 'insert_table_of_contents');

この関数では、まずページがシングル投稿ページであるかどうかを確認します。

次に、見出し要素を取得し、generate_table_of_contents関数を呼び出して目次を生成します。

最後に、最初のh2要素の上に目次を挿入します。

以上が、PHPコードの詳細な解説です。

次のセクションでは、コンテンツのフィルタリングについて説明します。

コンテンツのフィルタリング

目次を自動生成するためには、ページのコンテンツから見出し要素を正確に抽出し、それらに一意のIDを付与する必要があります。

このセクションでは、見出しのマッチングとIDの付与について詳しく解説します。

見出しのマッチングとIDの付与

見出し要素を抽出し、それらに一意のIDを付与するためには、正規表現とPHPのpreg_match_all関数を使用します。

以下のコードは、見出し要素をマッチングし、IDを付与する方法の例です。

$heading_pattern = '/<(h2|h3)(.*?)>(.*?)<\/(h2|h3)>/is';
$matches = array();
$heading_count = preg_match_all($heading_pattern, $content, $matches, PREG_SET_ORDER);

このコードでは、$contentにページのHTMLコンテンツが格納されており、$matchesに見出し要素が格納されます。

$heading_patternは、h2h3要素をマッチングするための正規表現です。

見出し要素がマッチングされた後、それぞれの見出しに一意のIDを付与します。

以下のコードは、見出し要素にIDを付与する方法の例です。

$headings = array();
$heading_counter = 0;
foreach ($matches as $match) {
    $headings[] = array(
        'tag' => $match[1],
        'attributes' => $match[2],
        'text' => $match[3],
        'id' => 'heading-' . $heading_counter
    );
    $heading_counter++;
}

このコードでは、$matches配列から見出し要素を取り出し、それぞれに一意のIDを付与しています。

IDはheading-というプレフィックスにカウンターを付けた形式で生成されます。

次に、見出し要素にIDを付与するために、preg_replace_callback関数を使用します。

以下のコードは、見出し要素にIDを付与する方法の例です。

$content = preg_replace_callback($heading_pattern, function ($match) use ($headings) {
    static $index = 0;
    $id = $headings[$index]['id'];
    $index++;
    return sprintf('<%1$s%2$s><span id="%3$s">%4$s</span></%1$s>', $match[1], $match[2], $id, $match[3]);
}, $content);

このコードでは、preg_replace_callback関数を使用して、見出し要素にIDを付与しています。

$headings配列からIDを取得し、<span>タグを使用して見出し要素に挿入します。

以上が、見出しのマッチングとIDの付与の詳細な解説です。

このプロセスにより、ページ内の見出し要素に一意のIDが付与され、目次から各見出しにスムーズにジャンプできるようになります。

次のセクションでは、コードの使い方について説明します。

コードの使い方

WordPressで目次を自動生成するためのPHPコードを実際に使用する方法について解説します。

このセクションでは、functions.phpへのコードの追加方法や設定のカスタマイズについて詳しく説明します。

functions.phpへの追加

目次を自動生成するためのPHPコードは、WordPressテーマのfunctions.phpファイルに追加します。

functions.phpは、テーマの機能を拡張するためのファイルで、テーマフォルダ内に存在します。

テーマフォルダ内の位置

functions.phpファイルは、使用しているテーマのフォルダ内にあります。

テーマフォルダは、通常以下のパスにあります。

/wp-content/themes/your-theme/

ここで、your-themeは使用しているテーマの名前です。

このフォルダ内にfunctions.phpファイルが存在します。

コードの挿入方法

functions.phpファイルを開き、以下のコードを追加します。

このコードは、目次を自動生成するための関数を定義し、ページのコンテンツに目次を挿入するためのフィルタを追加します。

function generate_table_of_contents($headings) {
    $table_of_contents = '<div class="toc__container"><p class="toc__title">目次</p><ol class="toc__list">';
    $current_h2_item = '';
    $has_h3 = false;
    foreach ($headings as $heading) {
        if ($heading['tag'] == 'h2') {
            if ($current_h2_item !== '') {
                if ($has_h3) {
                    $table_of_contents .= '</ol>';
                }
                $table_of_contents .= '</li>';
            }
            $current_h2_item = '<li class="toc__item toc__item--h2"><a href="#' . $heading['id'] . '" class="toc__link">' . $heading['text'] . '</a>';
            $has_h3 = false;
            $table_of_contents .= $current_h2_item;
        } else {
            if (!$has_h3) {
                $table_of_contents .= '<ol class="toc__sublist">';
                $has_h3 = true;
            }
            $table_of_contents .= '<li class="toc__item toc__item--h3"><a href="#' . $heading['id'] . '" class="toc__link">' . $heading['text'] . '</a></li>';
        }
    }
    if ($current_h2_item !== '') {
        if ($has_h3) {
            $table_of_contents .= '</ol>';
        }
        $table_of_contents .= '</li>';
    }
    $table_of_contents .= '</ol></div>';
    return $table_of_contents;
}
function insert_table_of_contents($content) {
    if (is_single()) {
        $heading_pattern = '/<(h2|h3)(.*?)>(.*?)<\/(h2|h3)>/is';
        $matches = array();
        $heading_count = preg_match_all($heading_pattern, $content, $matches, PREG_SET_ORDER);
        if ($heading_count >= 2) {
            $headings = array();
            $heading_counter = 0;
            foreach ($matches as $match) {
                $headings[] = array(
                    'tag' => $match[1],
                    'attributes' => $match[2],
                    'text' => $match[3],
                    'id' => 'heading-' . $heading_counter
                );
                $heading_counter++;
            }
            $content = preg_replace_callback($heading_pattern, function ($match) use ($headings) {
                static $index = 0;
                $id = $headings[$index]['id'];
                $index++;
                return sprintf('<%1$s%2$s><span id="%3$s">%4$s</span></%1$s>', $match[1], $match[2], $id, $match[3]);
            }, $content);
            $table_of_contents = generate_table_of_contents($headings);
            $first_h2_position = strpos($content, '<h2>');
            if ($first_h2_position !== false) {
                $content = substr_replace($content, $table_of_contents, $first_h2_position, 0);
            }
        }
    }
    return $content;
}
add_filter('the_content', 'insert_table_of_contents');

設定のカスタマイズ

このコードはデフォルトの設定で動作しますが、必要に応じてカスタマイズすることができます。

以下では、ページタイプの指定や目次生成の条件設定について説明します。

ページタイプの指定

目次を生成するページのタイプを指定することができます。

デフォルトでは、シングル投稿ページ(is_single())に対して目次を生成しますが、他のページタイプにも対応させることができます。

is_single()の変更

is_single()関数は、シングル投稿ページであるかどうかを判定します。

これを他の条件に変更することで、目次を生成するページタイプを変更できます。

例えば、固定ページ(is_page())に対して目次を生成する場合は、以下のように変更します。

if (is_page()) {
    // 目次生成のコード
}
カスタム投稿タイプの指定方法

カスタム投稿タイプに対して目次を生成する場合は、is_singular()関数を使用します。

例えば、カスタム投稿タイプがmy_custom_postである場合は、以下のように指定します。

if (is_singular('my_custom_post')) {
    // 目次生成のコード
}

目次生成の条件設定

目次を生成する条件を設定することができます。

デフォルトでは、ページ内に2つ以上のh2要素がある場合に目次を生成します。

if ($heading_count >= 2)の意味

この条件は、ページ内に2つ以上のh2要素がある場合に目次を生成することを意味します。

$heading_countは、ページ内の見出し要素の数を表します。

if ($heading_count >= 2) {
    // 目次生成のコード
}

最小見出し数の変更方法

目次を生成するための最小見出し数を変更することができます。

例えば、3つ以上のh2要素がある場合に目次を生成するように設定するには、以下のように変更します。

if ($heading_count >= 3) {
    // 目次生成のコード
}

このようにして、目次を生成するための条件を柔軟に設定することができます。

以上が、コードの使い方と設定のカスタマイズです。

次のセクションでは、プラグイン無しでの目次生成の利点について説明します。

スタイルの調整

テーマによっては初めからスタイルが適用されていますが、目次のデザインに対応していないテーマの場合、自分でCSSを記述する必要があります。

目次生成処理のコードにスタイルを適用することで、見た目を整え、ユーザーにとって使いやすい目次を作成することができます。このセクションでは、上記の目次生成コードにスタイルを適用する方法について解説します。

CSSを用いたスタイルの適用

まず、目次のコンテナ、タイトル、リスト、および各項目に対してCSSクラスが既に設定されています。これらのクラスに対してスタイルを適用することで、目次の見た目を調整できます。

以下は、目次にスタイルを適用するためのCSSの例です。

/* 目次全体のコンテナ */
.toc {
    background-color: #f9f9f9; /* 背景色を設定 */
    border: 1px solid #ddd; /* 枠線を設定 */
    padding: 10px; /* 内側の余白を設定 */
    margin-bottom: 20px; /* 下の余白を設定 */
    border-radius: 5px; /* 角を丸くする */
}

/* 目次のタイトル */
.toc-title {
    font-size: 1.2em; /* 文字サイズを設定 */
    font-weight: bold; /* 太字にする */
    display: block; /* ブロック表示にする */
    margin-bottom: 10px; /* 下の余白を設定 */
    cursor: pointer; /* カーソルをポインタにする */
}

/* 目次のチェックボックス(隠す) */
.toc-checkbox {
    display: none; /* 表示しない */
}

/* 目次の内容 */
.toc-content {
    display: none; /* 初期状態では非表示にする */
}

/* チェックボックスがチェックされた場合に目次を表示 */
.toc-checkbox:checked + .toc-title + .toc-content {
    display: block; /* 表示する */
}

/* 目次のリスト */
.toc-list {
    list-style-type: none; /* リストのデフォルトのスタイルを無効にする */
    padding-left: 0; /* 左側の余白をゼロにする */
}

/* 目次の各項目 */
.toc-list li {
    margin-bottom: 5px; /* 下の余白を設定 */
}

/* リンクのスタイル */
.toc-list a {
    text-decoration: none; /* 下線を無効にする */
    color: #0073aa; /* リンクの色を設定 */
}

.toc-list a:hover {
    text-decoration: underline; /* ホバー時に下線を表示する */
    color: #005177; /* ホバー時のリンクの色を設定 */
}

/* サブリストのスタイル */
.toc-list li ol {
    margin-left: 20px; /* 左の余白を設定 */
}

スタイルの適用手順

  1. CSSの追加:
    上記のCSSをあなたのテーマのCSSファイル(通常はstyle.css)に追加します。
  2. 目次生成コードの確認:
    既に目次生成コードには適切なクラスが設定されています。もしクラス名を変更したい場合は、目次生成コード内のクラス名も合わせて変更します。
  3. 目次の確認:
    テーマの適用後、実際のページを確認し、目次が期待通りのスタイルになっているかを確認します。
生成される目次の例

デザインが崩れる場合はテーマのCSSと競合している可能性が高いです。クラス名を違うものにするなどして対策しましょう。

デザインが反映されない場合はブラウザキャッシュの影響の可能性が高いのでCtrl + F5でスーパーリロードしてみてください。

目次にスタイルを適用することで、ユーザーエクスペリエンスが向上します。

CSSを用いて目次の見た目を調整し、統一感のあるデザインを目指しましょう。このガイドに従えば、簡単に目次のスタイルをカスタマイズできます。

以上が、WordPressで目次をプラグイン無しで自動生成するための実装手順です。

この手順に従うことで、簡単に目次を自動生成する機能を実装できます。

サイトのパフォーマンスやセキュリティを向上させるために、ぜひこの方法を試してみてください。

目次から探す