WordPressプラグインなしで目次を本文とサイドバーに追加する方法

「WordPressにプラグインなしで目次を追加したいんだけど、どうしたらできるの?」

WordPressのfunctions.phpに所定のコードを記入したら良いです。
私がネット検索で見つけたコードはh2からh3までの見出ししか目次にできなかったので、少し手を加えてh2からh5までの見出しを目次にできるようにしました。

余計なコードも記入しているので、不要な部分は適宜修正してご利用ください。

目次を本文に表示させる方法

以下を子テーマのfunctions.phpに記入してください。

子テーマのfunctions.phpに書き込むコード

/*---------------------------------*/
// 目次を追加
function my_add_content( $content ) {
  if ( is_single() ) {
    // 属性を持たないh2・h3・h4・h5要素を正規表現で表すパターン
    $pattern = '/<h[2-5]>(.*?)<\/h[2-5]>/i';// h2~h5までを検索。変更したい場合は[]内の数字を変更すれば良い
    // 本文の中から、すべてのh2・h3・h4・h5要素を検索
    preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER );
		
    // ページ内のh2・h3・h4・h5要素が1つ以上の場合に目次を出力
    if( count( $matches ) > 1 ){ 
      // 目次の出力に使用する変数
      global $toc2;// 別のfunction内でも使えるようにグローバル変数にする
      $toc = '<div id="toc-container"><div class="toc-title-container"><p class="toc-title">目次</p></div><nav><ul>';// 本文中の目次コード
      $toc2 = '<section id="toc-container-2" class="widget"><h2 class="widget-title toc-title-2">目次</h2><nav><ul>';// サイドバーの目次コード
      
      // 目次の階層の判断に使用する変数
      $hierarchy = NULL;
      // ループ回数を数える変数
      $i = 0;
			
      // 本文内のh2・h3・h4・h5要素を上から順番にループで処理
      foreach( $matches as $element ){ 
        // ループ回数を1加算
        $i++;
        // h2・h3・h4・h5に指定するIDの属性値を作成
        $id = 'chapter-' . $i;
        // h2・h3・h4・h5タグにIDを追加
        $chapter = preg_replace( '/<(.+?)>(.+?)<\/(.+?)>/',  '<$1 id ="' . $id . '">$2</$3>', $element[0] );
        // ページ内のh2・h3・h4・h5要素を、IDが追加されているh2・h3・h4・h5要素に置換
        $content = preg_replace( $pattern, $chapter, $content, 1);
				
        // 現在のループで扱う要素を判断する条件分岐
        if( strpos( $element[0], '<h2' ) === 0 ){ 
          $level = 0;
        }elseif( strpos( $element[0], '<h3' ) === 0 ){ 
          $level = 1;
        }elseif( strpos( $element[0], '<h4' ) === 0 ){ 
          $level = 2;
        }else{        
          $level = 3;
        }
				
        // サイドバーの目次が長くなりすぎるのを避けるため、サイドバーはh2だけを目次として扱うように設定した
        // 現在の状態を判断する条件分岐
        if( $hierarchy === $level ){ // hxがそれぞれ連続する場合
          $toc .= '</li>';
          $toc2 .= '</li>';
        }elseif( $hierarchy < $level ){ // 次が小見出しとなる場合
          switch($level){
            case 1:
              $toc .= '<ul>';
              $hierarchy = 1;
              break;
            case 2:
              $toc .= '<ul>';
              $hierarchy = 2;
              break;
            case 3:
              $toc .= '<ul>';
              $hierarchy = 3;
              break;
          }
        }elseif( $hierarchy > $level ){ // 次が親見出しとなる場合
          switch($hierarchy){
            case 1:
              switch($level){
                case 0:
                  $toc .= '</li></ul></li>';
                  $hierarchy = 0;
                  break;
              }
              break;
            case 2:
              switch($level){
                case 0:
                  $toc .= '</li></ul></li></ul></li>';
                  $hierarchy = 0;
                  break;
                case 1:
                  $toc .= '</li></ul></li>';
                  $hierarchy = 1;
                  break;
              }
              break;
            case 3:
              switch($level){
                case 0:
                  $toc .= '</li></ul></li></ul></li></ul></li>';
                  $hierarchy = 0;
                  break;
                case 1:
                  $toc .= '</li></ul></li></ul></li>';
                  $hierarchy = 1;
                  break;
                case 2:
                  $toc .= '</li></ul></li>';
                  $hierarchy = 2;
                  break;
              }
              break;
          }
        }elseif( $i == 1 ){ // ループ1回目の場合
          $hierarchy = 0;
        }
				
        // 目次の項目で使用する要素を指定
        $title = $element[1]; 
        // 目次の項目を作成。※次のループで<li>の直下に<ol>タグを出力する場合ががあるので、ここでは<li>タグを閉じていません。
        if($hierarchy == 0){
          $toc .= '<li><a href="#' . $id . '">' . $title . '</a>';
          $toc2 .= '<li><a href="#' . $id . '">' . $title . '</a>';
        }else{
          $toc .= '<li><a href="#' . $id . '">' . $title . '</a>';
        }
      }
			
      // 目次の最後の項目をどの要素から作成したかによりタグの閉じ方を変更
      if( $level == 0 ){
        $toc .= '</li></ul>';
        $toc2 .= '</li></ul>';
      }elseif( $level == 1 ){
        $toc .= '</li></ul></li></ul>';
        $toc2 .= '</li></ul>';
      }elseif( $level == 2 ){
        $toc .= '</li></ul></li></ul></li></ul>';
        $toc2 .= '</li></ul>';
      }elseif( $level == 3 ){
        $toc .= '</li></ul></li></ul></li></ul></li></ul>';
        $toc2 .= '</li></ul>';
      }

      $toc .= '</nav></div>';
      $toc2 .= '</nav></section>';

$ad1 = <<< EOF
EOF;

$ad2 = <<< EOF
EOF;

global $ad3;
$ad3 = <<< EOF
EOF;

      // 本文に目次と広告を追加
      $h2 = '/^<h2.*?>.+?<\/h2>$/im';//H2見出しのパターン
      if ( preg_match_all( $h2, $content, $h2s )) {//H2見出しが本文中にあるかどうか
        if ( $h2s[0] ) {//チェックは不要と思うけど一応
          if ( $h2s[0][0] ) {//1番目のH2見出し手前に目次を挿入
            $content  = str_replace($h2s[0][0], $toc.$h2s[0][0], $content);
          }
          if ( $h2s[0][0] ) {//1番目のH2見出し手前に広告を挿入
            $content  = str_replace($h2s[0][0], $ad1.$h2s[0][0], $content);
          }
          if ( $h2s[0][2] ) {//3番目のH2見出し手前に広告を挿入
            $content  = str_replace($h2s[0][2], $ad2.$h2s[0][2], $content);
          }
        }
      }
    }
  }
  return $content;
}
add_filter('the_content','my_add_content');
Code language: PHP (php)

目次をサイドバーに表示させる方法

目次をサイドバーに表示させたい場合は、以下のアクションフックを子テーマのsidebar.phpに追加してください。

子テーマのsidebar.phpに書き込むコード

<?php do_action( 'my_sidebar_action_hook' ); ?>
Code language: PHP (php)

ご参考までに、私が使っているテーマの子テーマのsidebar.phpの中身は以下のようになっています。

アクションフックを追加したら、子テーマのfunctions.phpに以下のコードを追記してください。

子テーマのfunctions.phpに書き込むコード

function my_sidebar_echo(){
  global $toc2;
  if($toc2){
    echo $toc2;
  }
}
add_action('my_sidebar_action_hook', 'my_sidebar_echo', 11);
Code language: PHP (php)

別のfunction内で作った変数を特定のfunction内で使いたいときは、どちらの変数もglobalを使ってグローバル変数として定義する必要があります。

広告を見出しの前に表示させる方法

子テーマのfunctions.phpに書き込むコードで表示している以下コードEOFの間に広告コードを追記してください。

$ad1 = <<< EOF
EOF;

$ad2 = <<< EOF
EOF;
Code language: PHP (php)

ご参考までに、当ブログで利用しているグーグルアドセンスの広告コードを追記した場合は以下のようになります。

当該コードを左に寄せていますが、これは私が別の案件でインデントを取った時に広告が表示されなかったことがあるからです。

1番目と3番目のH2見出し手前に広告を挿入するようになっています。
お好みに合わせて適宜変更してください。

広告を特定の場所に表示させる方法

広告を特定の場所に表示させたい場合は、以下のアクションフックを表示させたい場所に関係がある子テーマのphpファイルに追加してください。

子テーマのphpファイルに書き込むコード

<?php do_action( 'my_contents_bottom_action_hook' ); ?>
Code language: PHP (php)

子テーマのfunctions.phpに書き込むコード

function my_contents_bottom_echo(){
  global $ad3;
  if($ad3){
    echo $ad3;
  }
}
add_action('my_contents_bottom_action_hook', 'my_contents_bottom_echo', 12);
Code language: PHP (php)

これら以外にプラグインなしで目次を表示する方法はないの?

あります。
作った目次をグローバル変数にして、好きなタイミングで表示させれば良いです。

目次の作り方は同じです。
作り方の概要は以下の通りです。

目次の作り方の概要
  1. 本文を取得
  2. 本文の中から見出しを取得
  3. 見出しにidを付与
  4. 目次のhtmlを作成

あとはアクションフックやphpの関数を利用すれば、意外と簡単にどこでも好きなところに目次を表示させられるようになります。


はい、と言うことで、WordPressにプラグインなしで目次を追加するには、WordPressのfunctions.phpに所定のコードを記入したら良いです。
ここで説明している以外にも、ネット検索で色々な情報が見つかると思います。

目的に合ったものをご利用ください。

以上、「WordPressプラグインなしで目次を本文とサイドバーに追加する方法」でした。