SEOを強化するWordPressの目次の簡単自作法:プラグインは不要!

SEOを強化するWordPressの目次の簡単自作法:プラグインは不要!

ブログサイトのSEOで欠かせないのが目次。WordPressで目次を作成する場合はプラグインで実装するのが一般的だが、本記事では自作者向けにプログラムで実装する方法を紹介する。

プログラムで実装するメリットとしては以下2点あると思う。

・読み込みデータ量の最適化
・自由なデザイン性

プラグインの場合は簡単に実装できる代わりに、使わない機能やスタイルがサイト表示性能の劣化を引き起こす可能性がある。自作すればシンプルにして読み込みデータ量を最適化できるため、表示性能向上につなげることが可能。また、サイトデザインにしても自分で設定できるので自由度が高いのでメリットが大きい。頑張って作って見て欲しい。

この記事は、プラグインを使わずWordPressに目次を実装したいという方向けの記事である。

目次作成プログラムの概要

作成される目次は下記の通り、見出し毎に番号がつくように出力するプログラムである。

1.XXXXX
 1-1. XXXXXX
 1-2. XXXXXX
2. XXXXX

出力例はこの記事の目次で確認して欲しい。

目次作成プログラム(コピペOK)

早速プログラムを紹介する。

// コンテンツ内に目次を作成する関数
function create_table_of_contents($content) {
    // 初期変数の設定
    $matches = array();  // マッチした見出しタグを保存する配列
    $toc = '<div class="toc-container"><p class="toc-title">目次</p><div class="toc-list">';  // 目次のHTMLを生成する文字列
    $current_heading_level = 0;  // 現在処理中の見出しレベル
    $current_nesting_level = 0;  // 現在のネストレベル
    $h2_counter = 0;  // h2の数をカウントする変数
    $h3_counter = 0;  // h3の数をカウントする変数

    // コンテンツ内のh2とh3タグを全て検索し、マッチした結果を$matchesに格納
    if (preg_match_all('/<(h[2-3])[^>]*>(.*?)<\/\1>/s', $content, $matches, PREG_SET_ORDER)) {
        // 各見出しタグに対して
        foreach ($matches as $match) {
            // 見出しレベル(h2なら2、h3なら3)を取得
            $heading_level = intval(substr($match[1], 1));

            // 現在の見出しレベルと比較して目次のHTMLを作成
            if ($heading_level > $current_heading_level) {  // 新たに小見出しが始まった場合
                $toc .= '<ul class="toc-sublist">';
                $current_nesting_level++;
                $h3_counter = 0;  // h3のカウンタをリセット
            } elseif ($heading_level < $current_heading_level) {  // 小見出しが終わった場合
                $toc .= '</ul>';
                $current_nesting_level--;
            }

            // 現在の見出しレベルを更新
            $current_heading_level = $heading_level;
            // 見出しのテキストからIDを生成(URLフレンドリーな形式に)
            $id = sanitize_title($match[2]);

            // 見出しレベルに応じて目次のHTMLを生成
            if ($heading_level === 2) {
                $h2_counter++;
                $toc .= '<li class="toc-item"><a class="toc-link" href="#' . $id . '">' . $h2_counter . '. ' . $match[2] . '</a></li>';
            } else {
                $h3_counter++;
                $toc .= '<li class="toc-item"><a class="toc-link" href="#' . $id . '">' . $h2_counter . '-' . $h3_counter . '. ' . $match[2] . '</a></li>';
            }
            // コンテンツ内の見出しタグにIDを追加
            $content = str_replace($match[0], '<' . $match[1] . ' id="' . $id . '">' . $match[2] . '</' . $match[1] . '>', $content);
        }
        // 全ての見出しタグを処理した後、未閉じのulタグを閉じる
        for ($i = 0; $i < $current_nesting_level; $i++) {
            $toc .= '</ul>';
        }
        // 目次HTMLの最後を閉じる
        $toc .= '</div></div>';
    }
    // ページのコンテンツを最初のh2タグで分割
    $parts = explode('<h2', $content, 2);
    // h2タグが存在する場合としない場合で分岐
    if (count($parts) === 2) {
        // h2タグが存在する場合、目次をその前に挿入
        $content = $parts[0] . $toc . '<h2' . $parts[1];
    } else {
        // h2タグが存在しない場合、目次をコンテンツの前に挿入
        // $content = $toc . $parts[0];
        $content = $parts[0];
    }
    // 加工したコンテンツを返す
    return $content;
}
// 'the_content'フィルターに目次作成関数を追加
// WordPressがコンテンツを表示する際にこの関数を通過するようになる
add_filter('the_content', 'create_table_of_contents');

デザインは以下で指定。このサイトで表示しているままのデザインなのでフォントカラーやサイズなどは独自で指定して欲しい。カーソルがあたった時に色を変えたかったので「hover」を使用している。不要なら削除しても構わない。

/* 目次設定 */
.toc-container {
    border: 0.5px solid #f3f2ee;
    margin-bottom: 10px;
    margin-top: 20px;
    width: 80%;
    margin-left: auto;
    margin-right: auto;
}
p.toc-title {
    font-size: 19px;
    padding-left: 40px;
    margin-top: 5px;
    margin-bottom: 5px;
}
div.toc-list,
div.toc-list ul.toc-sublist {
    list-style: none;
}
a.toc-link {
    text-decoration: none;
    color: white;
}
a.toc-link:hover {
    text-decoration: none;
    color: #3a4cefd6;
}

スマホ表示デザインについても独自で指定してあげる必要があるのだが、一応紹介しておく。

/* スマホ画面のみに適用するスタイル */
@media (max-width: 767.98px) {
   /*目次*/
    p.toc-title {
        font-size: 19px;
        padding-left: 0px;
        margin-bottom: 5px;
    }
    .toc-list {
        padding-left: 0px;
    }
    li.toc-sublist {
        padding-top: 3px;
        padding-bottom: 3px;
    }
    ul.toc-sublist {
        font-size: 13px;
        padding-left: 16px;
        padding-top: 3px;
        padding-bottom: 3px;
    }
}

目次作成プログラム補足

今回のプログラムは「the_content」関数のフィルターフックを使用して、「the_content」でブログ内容を読み込む前に処理を実行して自動で目次を追加している。

処理概要としては記事のHTML($content)を読み込み、「h2」「h3」を正規表現を使用してピックアップして配列に格納する。

if (preg_match_all('/<(h[2-3])[^>]*>(.*?)<\/\1>/s', $content, $matches, PREG_SET_ORDER)) {

h3要素は直前のh2要素の子要素にしたいのでh3の場合「$current_nesting_level」は0、h2の場合は1という具合にネストの深さに応じてレベル管理している。

if ($heading_level > $current_heading_level) {
  $toc .= '<ul class="toc-sublist">';
  $current_nesting_level++;
  $h3_counter = 0;
} elseif ($heading_level < $current_heading_level) {
  $toc .= '</ul>';
  $current_nesting_level--;
}

目次のリンクと紐付けるため下記処理で見出しにIDを付与している。

// コンテンツ内の見出しタグにIDを追加
$content = str_replace($match[0], '<' . $match[1] . ' id="' . $id . '">' . $match[2] . '</' . $match[1] . '>', $content);

目次は最初に来るh2要素の直前に配置したいので下記処理でcontentのHTML内に目次を埋め込む処理を下記で実装している。

// ページのコンテンツを最初のh2タグで分割
$parts = explode('<h2', $content, 2);
// h2タグが存在する場合としない場合で分岐
if (count($parts) === 2) {
  // h2タグが存在する場合、目次をその前に挿入
  $content = $parts[0] . $toc . '<h2' . $parts[1];
} else {
  // h2タグが存在しない場合、目次をコンテンツの前に挿入
  // $content = $toc . $parts[0];
  $content = $parts[0];
}

目次作成処理まとめ

一般的にWordPressでの目次作成はプラグインで実現することが多いのだが、今回は自分でプログラムで自作する方法を説明した。

目次作成処理を自作する利点としては圧倒的な自由度にあるだろう。サイト表示パフォーマンスの改善を行う場合、不要なプログラムはできるだけ少ない方が良い。自作プログラムの場合は必要最小限のコーディングを行えば良いので最適化しやすい。

パフォーマンス・チューニングが自分でできて、他のどんなサイトよりも高性能にできる可能性があるという点に面白さを感じるのだ。

目次自作はそんなに難しいものではない。結局やるやつはやるし、やらないやつはやらないのだ。

以上、記事を最後まで読んでくれて感謝。