WordPress プラグインなしで記事の見出しから目次を作成

WordPress でプラグインを使用しないで、記事の見出しから目次を作成する方法を紹介します。

ググってみると既に多くのサイトで紹介されていますが、JavaScript (jQuery) による実装ばかりだったので、ここでは PHP コード (functions.php) による実装(目次を作成する部分)をしてみました。

PHP コード

functions.php などに下記のコードを記述します。

/**
 * 目次ショートコードです。
 *
 * @version 4.2.1
 */
class Toc_Shortcode {

	private $add_script = false;
	private $atts = array();

	public function __construct() {
		add_shortcode( 'toc', array( $this, 'shortcode_content' ) );
		add_action( 'wp_footer', array( $this, 'add_script' ), 999999 );
		add_filter( 'the_content', array( $this, 'change_content' ) );
	}

	function change_content( $content ) {
		$elements = wp_html_split( $content );
		$id = 1;
		foreach ( $elements as &$element ) {
			if ( 0 === strpos( $element, '<h' ) ) {
				if ( preg_match( '/<h[1-6].*?>/u', $element ) ) {
					if ( ! preg_match( '/<h[1-6](.*?) id="([^"]*)"/u', $element ) ) {
						$s = preg_replace( '/<(h[1-6])(.*?)>/u', '<${1} id="toc' . $id . '" ${2}>', $element );
						if ( $element !== $s ) {
							$element = $s;
						}
						
					}
					$id++;
				}
			}
		}
		return join( $elements );
	}

	public function shortcode_content( $atts ) {
		global $post, $page;

		if ( ! isset( $post ) )
			return '';

		$this->atts = shortcode_atts( array(
			'id'        => '',
			'class'     => 'toc',
			'title'     => '目次',
			'toggle'    => true,
			'opentext'  => '開く',
			'closetext' => '閉じる',
			'close'     => false,
			'showcount' => 2,
			'depth'     => 0,
			'toplevel'  => 2,
			'scroll'    => 'smooth',
		), $atts );

		$this->atts['toggle'] = ( false !== $this->atts['toggle'] && 'false' !== $this->atts['toggle'] ) ? true : false;
		$this->atts['close'] = ( false !== $this->atts['close'] && 'false' !== $this->atts['close'] ) ? true : false;

		$content = $post->post_content;
		$content = function_exists( 'do_blocks' ) ? do_blocks( $content ) : $content;

		$split = preg_split( '/<!--nextpage-->/msuU', $content );
		$pages = array();
		$permalink = get_permalink( $post );

		if ( is_array( $split ) ) {
			$page_number = 0;
			$counter = 0;
			$counters = array( 0, 0, 0, 0, 0, 0 );
			$current_depth = 0;
			$prev_depth = 0;
			$top_level = intval( $this->atts['toplevel'] );
			if ( $top_level < 1 ) $top_level = 1;
			if ( $top_level > 6 ) $top_level = 6;
			$this->atts['toplevel'] = $top_level;
			$max_depth = ( ( $this->atts['depth'] == 0 ) ? 6 : intval( $this->atts['depth'] ) );

			$toc_list = '';

			foreach ( $split as $content ) {
				$headers = array();
				preg_match_all( '/<(h[1-6])(.*?)>(.*?)<\/h[1-6].*?>/u', $content, $headers );
				$header_count = count( $headers[0] );
				$page_number++;

				for ( $i = 0; $i < $header_count; $i++ ) {
					$depth = 0;
					switch ( $headers[1][$i] ) {
						case 'h1': $depth = 1 - $top_level + 1; break;
						case 'h2': $depth = 2 - $top_level + 1; break;
						case 'h3': $depth = 3 - $top_level + 1; break;
						case 'h4': $depth = 4 - $top_level + 1; break;
						case 'h5': $depth = 5 - $top_level + 1; break;
						case 'h6': $depth = 6 - $top_level + 1; break;
					}
					if ( $depth >= 1 && $depth <= $max_depth ) {
						if ( $current_depth == $depth ) {
							$toc_list .= '</li>';
						}
						while ( $current_depth > $depth ) {
							$toc_list .= '</li></ul>';
							$current_depth--;
							$counters[$current_depth] = 0;
						}
						if ( $current_depth != $prev_depth ) {
							$toc_list .= '</li>';
						}
						if ( $current_depth < $depth ) {
							$class  = $current_depth == 0 ? ' class="toc-list"' : '';
							$hidden = $current_depth == 0 && $this->atts['close'] ? ' hidden=""' : '';
							$toc_list .= "<ul{$class}{$hidden}>";
							$current_depth++;
						}
						$counters[$current_depth - 1]++;
						$number = $counters[0];
						for ( $j = 1; $j < $current_depth; $j++ ) {
							$number .= '.' . $counters[$j];
						}
						$counter++;

						if ( preg_match( '/.*? id="([^"]*)"/u', $headers[2][$i], $m ) ) {
							$href = '#' . $m[1];
						} else {
							$href = '#toc' .  ( $i + 1 );
						}

						if ( $page != $page_number ) {
							if ( 1 == $page_number ) {
								$href = trailingslashit( $permalink ) . $href;
							} else {
								$href = trailingslashit( $permalink ) . $page_number . '/' . $href;
							}
						}

						$toc_list .= '<li' . ( $page !== $page_number ? ' class="other-page"' : '' ) . '>';
						$toc_list .= '<a href="' . esc_url( $href ) . '"><span class="contentstable-number">' . $number . '</span> ' . strip_shortcodes( $headers[3][$i] ) . '</a>';

						$prev_depth = $depth;
					}
				}
			}

			while ( $current_depth >= 1 ) {
				$toc_list .= '</li></ul>';
				$current_depth--;
			}
		}

		$html = '';
		if ( $counter >= $this->atts['showcount'] ) {
			$this->add_script = true;

			$toggle = '';
			if ( $this->atts['toggle'] ) {
				$toggle = ' <span class="toc-toggle">[<a class="internal" href="javascript:void(0);">' . ( $this->atts['close'] ? $this->atts['opentext'] : $this->atts['closetext'] ) . '</a>]</span>';
			}

			$html .= '<div' . ( $this->atts['id'] != '' ? ' id="' . $this->atts['id'] . '"' : '' ) . ' class="' . $this->atts['class'] . '">';
			$html .= '<p class="toc-title">' . $this->atts['title'] . $toggle . '</p>';
			$html .= $toc_list;
			$html .= '</div>' . "\n";
		}

		return $html;
	}

	public function add_script() {
		if ( ! $this->add_script ) {
			return false;
		}

		$var = wp_json_encode( array(
			'open_text' => isset( $this->atts['opentext'] ) ? $this->atts['opentext'] : '開く',
			'close_text' => isset( $this->atts['closetext'] ) ? $this->atts['closetext'] : '閉じる',
			'scroll' => isset( $this->atts['scroll'] ) ? $this->atts['scroll'] : 'smooth',
		) );

		?>
<script<?php echo current_theme_supports( 'html5', 'script' ) ? '' : " type='text/javascript'"; ?>>
var xo_toc = <?php echo $var; ?>;
let xoToc = () => {
  /**
   * スムーズスクロール関数
   */
  let smoothScroll = (target, offset) => {
    const targetRect = target.getBoundingClientRect();
    const targetY = targetRect.top + window.pageYOffset - offset;
    window.scrollTo({left: 0, top: targetY, behavior: xo_toc['scroll']});
  };

  /**
   * アンカータグにイベントを登録
   */
  const wpadminbar = document.getElementById('wpadminbar');
  const smoothOffset = (wpadminbar ? wpadminbar.clientHeight : 0) + 2;
  const links = document.querySelectorAll('.toc-list a[href^="#"]');
  for (let i = 0; i < links.length; i++) {
    links[i].addEventListener('click', function (e) {
      const href = e.currentTarget.getAttribute('href');
      const splitHref = href.split('#');
      const targetID = splitHref[1];
      const target = document.getElementById(targetID);

      if (target) {
        e.preventDefault();
        smoothScroll(target, smoothOffset);
      } else {
        return true;
      }
      return false;
    });
  }

  /**
   * 目次項目の開閉
   */
  const tocs = document.getElementsByClassName('toc');
  for (let i = 0; i < tocs.length; i++) {
    const toggle = tocs[i].getElementsByClassName('toc-toggle')[0].getElementsByTagName('a')[0];
    toggle.addEventListener('click', function (e) {
      const target = e.currentTarget;
      const tocList = tocs[i].getElementsByClassName('toc-list')[0];
      if (tocList.hidden) {
        target.innerText = xo_toc['close_text'];
      } else {
        target.innerText = xo_toc['open_text'];
      }
      tocList.hidden = !tocList.hidden;
    });
  }
};
xoToc();
</script><?php
	}

}

$toc = new Toc_Shortcode();

ショートコード

投稿または固定ページの表示したい場所に toc ショートコードを記述します。

[toc]

使用例:

[toc title="目次" depth="2"]

オプション

オプションデフォルト説明
id“”ID です。
class“toc”クラスです。
省略(”” を指定) することはできません。
title“目次”目次のタイトルです。
toggle“true”目次リストの開閉リンクを表示するかどうか。
表示する場合 “true”、表示しない場合は “false”。
opentext“開く”開くリンクのテキストです。
closetext“閉じる”閉じるリンクのテキストです。
close“false”最初に目次リストの開閉リンクを閉じた状態にするかどうか。
閉じた状態の場合は “true”、開いた状態の場合は “false”。
showcount2目次を表示する見出し項目の数(以上)です。
depth0出力する階層数(階層レベルではありません)です。
0 の場合はすべての階層を表示します。
toplevel=”2″ depth=”4″ の場合は、h2, h3, h4, h5 のヘッダーが対象となります。
toplevel2トップの階層のヘッダー(1 から 6)を指定します。
scroll“smooth”スクロールの動作を指定します。
スクロールするのに滑らかにアニメーションする場合は “smooth”、一回のジャンプで瞬時に行う場合は “auto”。

スタイル(CSS)

スタイル(CSS)の例です。テーマの style.css 等に記述してください。

.toc {
    width: auto;
    display: table;
    margin: 0 0 10px;
    padding: 10px;
    color: #333;
    word-break: break-all;
    word-wrap: break-word;
    border: #ccc solid 1px;
    border-radius: 3px;
    background-color: #fafafa;
}
.toc .toc-title {
    margin: 0;
    padding: 0;
    text-align: center;
    font-weight: bold;
}
.toc .toc-toggle {
    font-weight: normal;
    font-size: 90%;
}
.toc ul {
    list-style: none;
}
.toc .toc-list {
    margin: 0;
    padding: 0;
}
.toc .toc-list .other-page a {
	color: gray;
}

サンプルイメージ

toc

投稿に目次を自動挿入

下記コードは、全ての投稿に目次を自動挿入するサンプル コードになります。

このサンプル コードでは、全ての投稿(シングル)ページの h2 ヘッダー(見出し2)の直前に目次を挿入しています。

function add_toc_content( $content ) {
	if ( is_single() ) {

		$shortcode = '[toc showcount="4"]';

		$pattern = '/<h2.*?>/i';
		if ( preg_match( $pattern, $content, $matches ) ) {
			$content = preg_replace( $pattern, $shortcode . $matches[0], $content, 1 );
		}
	}
	return $content;
}

add_filter( 'the_content', 'add_toc_content', 10 );

追記

日付 / バージョン説明
2016年4月20日jQuery バージョン 1.12 でエラーが発生する不具合を修正しました。
2017年3月9日ヘッダーが順番に並んでいない場合に正しく目次を作成できない不具合を修正しました。
2017年3月12日コードを改修しました(修正はなし)。念のため jQuery をエンキュー(組み込み)する処理を追加しました。
2017年7月7日目次の開閉リンクをクリックすると、ページトップへスクロールする不具合を修正しました。
2017年11月23日最初に目次の開閉リンクを閉じた状態にするためのオプションを追加しました。
2017年12月4日 / 1.0.0 閉じた状態オプションを指定した場合に正しく目次を作成できない不具合を修正しました。
2017年12月5日 / 1.1.0ページに目次を複数配置できるように変更しました。
2018年8月20日 / 2.0.0目次を作成する対象のコンテンツを自動取得するように変更しました。これに伴い、targetclass パラメータを削除しました。
2019年6月30日 / 2.0.1「投稿に目次を自動挿入」の場合、アンカーリンクにスクロールしない不具合を修正しました。
2019年9月17日 / 2.0.2JavaScript をフッターに移動しました。
2021年4月19日 / 3.0.0jQuery を使用しない (組み込まない) ようにしました。
2022年4月16日 / 3.0.1再利用ブロック(見出しを含む)が存在する場合に目次が正しく作成されない不具合を修正しました。
2022年6月29日 / 4.0.0ショートコードで出力されるヘッダータグを除外するようにしました。
2022年7月11日 / 4.0.1区切りブロックを含むコンテンツで、目次が正しく作成されない不具合を修正しました。
2022年8月2日 / 4.1.0ページ分割(ページ区切り)に対応しました。
2023年2月3日 / 4.1.2depth オプションの不具合を修正しました。
2023年2月6日 / 4.2.0目次項目にクラスを追加しました。
2023年9月22日 / 4.2.1close オプションの不具合を修正しました。

コメント

  • 石鷹さんが「WordPress プラグインなしで記事の見出しから目次を作成(https://xakuro.com/blog/wordpress/277)」で紹介されているPHPコードを使わせて頂いたのですが、どうしても目次の値がすべて「0」になってしまいます。目次の値を1,2,3…と順に並ぶようにしたいのですが、どうすればよいでしょうか。ご教授お願い致します。ちなみに、ショートコードは現在、[toc title="目次" toplevel="2" depth="2"]と入力しています(目次をh2とh3だけで作りたいから)。

    • すみません、コードに不具合がありました。
      コードを修正しましたので、もう一度お試しください。
      お手数をおかけして申し訳ございません。m(__)m

      • 早速ご対応いただき誠にありがとうございました。ただ、先日と同じコード([toc title=”目次” toplevel=”2″ depth=”2″])を入力したところ、h2でしか目次が生成されず(下層のh3では目次が生成されない)、各目次リンクをクリックしても各見出しに移動しないという現象が起こりました。とくに急を要しているわけではないので、時間をかけてゆっくりと対処していただいてかまいません。こちらでもコードの不具合を修正できないか試みてみます。

    • 度々すみません。指定した階層よりも実際の階層が深い場合に正しく動作しませんでした。
      とりあえず修正したもとをアップしましたが、週末にでもじっくり動作確認したいとおもっているので、お急ぎでなければ、しばらくお待ちください。
      それにしても改めてソースををみてみるとぐちゃぐちゃしていて汚いなと反省しています。

      • そんなことないですよ。石鷹さんのコードなら「PageSpeed Insights」 にも「The W3C Markup Validation Service」にも引っかからないので、とても助かっています。WordPressの目次作成プラグイン「Table of Contents」だとどちらか一方に必ず引っかかってしまうんですよ。気長に待っているので、どうか無理しないでくださいね。

    • 一通りの動作確認してみましたが、正しく動作しているようです。
      ただし、ちょっとだけコードを(シンプルに)改修し、念のため jQuery を組み込む処理を追加したものをアップしたので、今回のものを使用してみてください。
      お手数かけます。m(__)m

  • 石鷹さんはじめまして。
    ショートコードを[toc title="目次" depth="0"]で使用しました。
    すると閉じる、開くを押すとページトップにスクロースしてしまうのですが、スクロールしない事は出来ないでしょうか。Javascriptで何かソースが必要でしょうか。

    お忙しいところすみません。よろしくお願いします。

    • こんにちは
      開閉リンクのリンク先(href)を “#” にしていたためスクロールしていました。
      スクロールしないほうがいいですよね。というかバグですね。コードは修正しておきます。
      修正箇所は下記の通りです。
      101行目
      $toggle = ' <span class="toc-toggle">[<a class="internal" href="#">' . $this->atts['closetext'] . '</a>]</span>';
      $toggle = ' <span class="toc-toggle">[<a class="internal" href="javascript:void(0);">' . $this->atts['closetext'] . '</a>]</span>';

      • ありがとうございます!!開く、閉じるを押してもページトップにスクロースされなくなりました。

        あと、もうひとつ質問なのですが、目次のタイトルがダブルコーテーションで囲まれて表示されてしまいます。該当すると思われる箇所のtoc-titleのソースを見たのですがダブルコーテーションがどこにも記述されていないように見えるのにダブルコーテーションが表示されてしまいます。

        キャッシュの問題なのでしょうか?できれがこのダブルコーテーション消したいのですが手段はありますか?

    • ダブルコーテーション(”)が全角になっているのかもしれません。チェックしてみてください。
      それから、[toc title="目次" depth="0"] でしたら、デフォルトのパラメーターなので [toc] とするだけでも OK ですよ。

  • この記事のコードを実際にfunction.phpに書いてみたのですが、そのまま貼ると何故かページの読み込みが出来なくなってしまいます。
    Twenty Twelveをベースにカスタマイズしているのですが、何か考えられる原因は思い当たりますでしょうか。
    php初心者なりに努力しているのですが、どうもうまく行かず。。
    アドバイスやヒントを頂ければ幸いです。

    • こんにちは
      当該コードの先頭の <?php の行を削除してみてください。
      削除してもページが読み込めない場合は、下記ページを参考にエラーを確認してみてください。
      https://wpdocs.osdn.jp/WordPressでのデバッグ
      当該コードのエラーはもちろん、それ以外でも、なるでく対応していきたいとおもいますので、返信お待ちしております。ではでは。

      • 返信ありがとうございます。
        さっそく先頭行を削除してみたところ無事ページは表示されました。

        しかし「閉じる」や、h2,3タグまでのジャンプが効かない状態になっています。今現在こちらでも試行錯誤しているところです。

      • もう一度トライしたところ無事表示されました!
        大変丁寧に対応して頂きありがとうございました!

        • おっ、よかった!
          今後も何かありましたら気軽にコメントしてください。

  • 初めまして。
    プラグインなしで目次を作りたくて、こちらの記事にたどり着きました。
    簡単に目次が作れて非常に感謝しています。

    1点質問なのですが、はじめに目次を閉じた状態にするにはどこのコードを変えれば良いのでしょうか。
    お手隙で構いませんので、返信いただけたら嬉しいです!

    • こんにちは
      おっ、そうですよね初めに閉じた状態がいい場合ありますよね。
      早速、初めに閉じた状態にするためのショートカットのオプション(close)を追加しました。詳細は本文を参照してください。
      なお、デフォルトでは開いた状態としたのでオプションを指定するか、コードの 'close' => false;(26 行目)の false を true にしてください。

  • はじめまして!使い易い目次のコード、ありがとうございます!
    早速導入させて頂いたのですが、目次の数字の消し方がわかりません・・・。
    教えていただければ幸いです。

    • こんにちは、コメントありがとうございます。
      目次の数字は、下記のスタイル(CSS)で消すことができます。テーマの style.css またはカスタマイズの追加 CSS 等に追記してみてください。
      .toc .contentstable-number { display: none; }

      • .toc を入れ忘れておりました!!!
        おかげさまで消すことができました。ご親切にありがとうございました。

  • 優良テーマの「TCD」の「gorgeous」にテーマを変更したら、それまできちんと動いていた目次移動が、目次の表示はできるのですがクリックしても移動しなくなってしまいました。何か方法はないでしょうか?

    • こんにちは、コメントありがとうございます。
      有料テーマなので検証できていませんが、
      [toc targetclass="post"]
      では、どうでしょうか。
      コードを変更する場合は、40行目の
      'targetclass' => 'entry-content',
      を、
      'targetclass' => 'post',
      へ変更してみてください。

      • 出来ました!

        テーマを変えたら突然動かなくなり、何日も悩んでいたので感動です!本当にありがとうございました。

  • はじめまして。
    コード使用させていただいております。
    ありがとうございます。

    ご質問です。
    h2タグより下の(h3以下)階層で目次を表示させたいと考えております。
    表示自体は出来るのですが、h2タグに入っている「id=”toc2″」が、目次のh3を表示しているリンクに入ってしまいます。

    お手数をおかけいたしますが、ご教授いただけますでしょうか。

    • こんにちは、初めまして。
      すみません、ちょっと症状を把握できていません。目次の表示は正しいがリンクが正しくないということでしょうか?
      もう少し具体的(ショートコードのパラメータやヘッダー構成など)に教えてもらえませんか。

      ショートコード:
      [toc toplevel=3]

      ヘッダー構成:
      ヘッダー2
       ヘッダー3
       ヘッダー3
        ヘッダー4
       ヘッダー3
      ヘッダー2
       ヘッダー3

      • 迅速なお返事ありがとうございます。
        現状をお伝えいたします。

        HTML内の自動ID挿入で、
        ヘッダー1(h1)タグに「id=”toc1″」、
        ヘッダー2(h2)タグに「id=”toc2″」、
        ヘッダー3(h3)タグに「id=”toc3″」、
        が挿入されており、

        目次のリンク生成の始まりが「a href=”#toc2″」
        となっております。

        「’toplevel’ => 3,」でヘッダー3(h3)タグからスタートさせたいです。

        ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
        ‘showcount’ => 0,
        ‘depth’ => 2,
        ‘toplevel’ => 3,

        $shortcode = ‘[toc showcount="4"]‘;
        $pattern = ‘//i’;

        ヘッダー構成:
        ヘッダー1
         ヘッダー2
          ヘッダー3
           ヘッダー4
          ヘッダー3
          ヘッダー3

        説明が下手ですがわかりますでしょうか?

    • 「投稿に目次を自動挿入」のコードにて、自動挿入する場合のことでしょうか?
      この場合は、

      $shortcode = '[toc showcount="4"]';

      $shortcode = '[toc toplevel="3"]';

      目次を挿入する位置をヘッダー3の直前にする場合は、

      $pattern = '/<h2.*?>/i';

      $pattern = '/<h3.*?>/i';

      となります。

      • ありがとうございました。

        はい。
        投稿に目次を自動挿入です。

        「’targetclass’ => ”,」の設定をh1タグのクラスがないところに変更したらうまくいきました。

        もうひとつ教えていただきたいのですが、「showcount」はどんな設定になりますでしょうか?

        • 何度もすみません。

          やはりリンクタグの取得がずれてしまいます。
          サイトリンクをお送りしますので、一度ご確認いただけますでしょうか?

          お手数をおかけいたしますが、何卒よろしくお願いいたします。

          • コードを下記のように変更してみてください。

            'targetclass' => 'entry-content',

            'targetclass' => 'article-body',

            showcount は、この値より目次の項目が少ない場合に目次を表示しないようにするパラメータです。

  • ご回答ありがとうございます。
    ‘targetclass’ => ‘article-body’,に変更いたしました。
    ありがとうございます。

    既定のhタグに「toc」を挿入しないようにすることはできますでしょうか?

    • 「既定のhタグ」とはどこのことかしら?
      過去の投稿に目次を自動挿入しないということかしら?この場合は「投稿に目次を自動挿入」のコードは不要です。目次を挿入したい投稿の場所にショートコードを記述してください。

      • お返事遅くなりすみません。
        サイトURLのページでは「MEMO」のh3タグにtoc6が挿入されています。
        このtoc6が目次に表示されていないためにリンクタグが1つずれているものと思われます。

        「MEMO」のh3タグにtocを追加しないようにすることはできませんでしょうか?

        • 実はあれからバージョンアップ (2.0.0) しました。
          以前は、targetclass パラメータで目次の対象とする場所を指定していましたが、これがコンテンツと一致しない場合に不具合が起こることがありました。
          お手数ですが、コードを更新してみてください。なお、targetclass パラメータは不要となったので削除してください。

          • お世話になります。

            バージョンアップしたコードを使用してみましたが、Hタグにtocクラスが追加されなくて、目次のリンクを押してもスクロールしません。

            何度も何度もすみませんが、ご確認お願いできますでしょうか。

          • 前バージョンのコードでカスタマイズした箇所が残っているようです。
            下記のように変更してみてください。
            $(".article-body :header").each(function () {

            $("#toc_content :header").each(function () {

          • すみません。
            前のバージョンに戻していました…。

            変更してみましたが、うまく動作しません。

            何度もすみませんが、何卒よろしくお願いいたします。

          • 同じ構成のコンテンツを用意して、検証してみたのですが、正しく動作するんですよね。
            謎です。もうすこし検証作業をしてみるつもりですが・・・時間がかかりそうです。
            お手数おかけしてすみません。

          • $content = “{$content}”;

            このコードは何を意味しますでしょうか?

          • $content = "<div id=\"toc_content\">{$content}</div>";
            ですかね。
            これは、目次の対象とするコンテンツを明確に特定するために、コンテンツをラップするタグ(#toc_content)を追加する処理です。
            以前は、パラメータで指定するようにしていましたが、テーマによって変更する必要があったため、このような処理にしました。

          • お世話になります。

            ご回答ありがとうございました。

            他のサイトでテストしてみましたが、動作しませんでした。
            もしかしたらテーマに問題があるのかもしれませんね。
            賢威というテーマを使用しています。

            また何か進展がありましたらご連絡させていただきます。
            ご丁寧な対応感謝しています。引き続きよろしくお願いいたします。

          • 賢威テーマに備わっている目次機能と競合しているのかもしれません。
            有料テーマなのでコードを確認できないため、断定はできませんが・・・。
            お役に立てずにすみません。

  • こんにちは。
    プラグイン無しで目次を表示させる方法を探していたらここにたどり着きました。

    改ページ(ページ分割)タグを入れた時、ほとんどのプラグインがそのページにある見出ししか目次に挿入されません。
    次のページの見出しは次のページの目次に、というような形になります。
    Extended Table of Contents (with nextpage support)というプラグインは分割した2ページ目の見出しもすべて1つの目次内に入るのですが、日本語に対応できていないようです。
    分割したページにも対応する目次にするには、どのようにすればいいかおわかりになりますでしょうか?

    • こんにちは、コメントありがとうございます。
      申し訳ありませんが、今のところ対応する予定はありません。m(__)m
      (時間が取れたら・・・)

      • わざわざ返信ありがとうございます^^
        日本語でページ分割に対応するプラグインは今のところ1つもないので、
        プラグイン化したらかなりの数使われると思います。
        もしお時間があって気が向いたらプラグイン化ぜひお願いします!
        (勝手なお願いすみません。。)

  • お世話になります。
    記事中にある「投稿に目次を自動挿入」というコードをfunction.phpに貼り付けました。目次は表示されるのですが、各目次へのリンクが動作しません。
    ちなみに記事投稿ページで[toc]とするとちゃんと各目次をクリックした際に該当の目次まで飛ぶのですが・・・。
    「投稿に目次を自動挿入」のコードはfunction.phpに書くものではないのでしょうか?

    • こんにちは、コメントありがとうございます。
      すみません、コードに不具合がありました。m(__)m
      コードを修正(バージョン 2.0.1)しましたので、今一度お試しください。

      • 石鷹 様
        迅速なご対応ありがとうございます!
        問題無く表示されることを確認いたしました。プラグインを使わずに目次が清々でき、大変助かっております。

  • お世話になります。
    質問申し訳ありません。
    タグ文章内にタグが入るとタグを
    目次として拾ってくれません。

    どの様に変更すれば良いでしょうか?

    • ご返信ありがとうございます。
      <h4>タグでした。
      パラメータ
      <?php echo do_shortcode(‘[toc toplevel="3" depth="5"]‘); ?>
      タグ
      <h4>○○○○○○○○○<br>○○○○○○○</h4>
      パラメータとタグは上記の様になっています。

      • 同じ条件で検証してみましたが、問題なく目次として拾ってきました。
        ちょっと分からないですね。もう一度、コードを貼り直して確認してみてもらえませんか?
        お手数おかけしてすみません。m(__)m

        • こちらこそお手数おかけします。
          試してみます。
          ありがとうございました。

  • お世話になります。
    質問申し訳ありません。
    <h3></h3>タグ文章内に<br>タグが入ると<h3></h3>タグを
    目次として拾ってくれません。

    どの様に変更すれば良いでしょうか?

    • こんにちは、コメントありがとうございます。
      返信が遅くなってすみません m(__)m
      ヘッダータグ内にタグが含まれている場合も、目次として拾う作りにはなっています。h3 タグに br タグを含めて検証してみましたが、問題なく目次として拾ってきました。
      もう少し具体的(使用テーマ、HTML、ショートコードのパラメータなど)に教えてもらえますか?

  • お世話になります。
    質問申し訳ありません。

    投稿に目次を自動挿入にて、目次を「h2 ヘッダー(見出し2)の直前」ではなく、先頭に挿入したい場合はどの様に変更すれば良いでしょうか?

    ご教授宜しくお願いします。

    • こんにちは、コメントありがとうございます。

      add_toc_content() を下記のように変更すると、投稿(コンテンツ)の先頭に挿入されます。

      function add_toc_content( $content ) {
      if ( is_single() ) {
      $shortcode = '[toc]';
      $content = $shortcode . $content;
      }
      return $content;
      }
      add_filter( 'the_content', 'add_toc_content', 10 );

  • 石鷹さんはじめまして。
    プラグイン無しで目次を表示させる方法を探していたらこちらのページにたどり着きました。
    .toc .contentstable-number { display: none; }をCSSに記述すると、すべての目次番号が消えますが、h2のみ目次番号を残す(h3以下を消す)方法はありますでしょうか?
    また、目次リンクをクリックすると一旦ページTOPを表示してから、当該箇所まで降りていく挙動を起こします。
    なにか解決方法がありましたら、お手すきの際にご教示いただけますと幸いです。
    よろしくお願いいたします。

    • こんにちは、コメントありがとうございます。
      >h2のみ目次番号を残す(h3以下を消す)方法はありますでしょうか?
      h2 のみを表示する場合は、下記のショートコードでできると思います
      [toc depth=1]
      ※ 目次が h1 からなら depth=2。
      >目次リンクをクリックすると一旦ページTOPを表示してから、当該箇所まで降りていく挙動を起こします。
      現象を再現できませんでしたが、今回、大幅に PHP コードを変更したので改善されているかもしれません。再度、PHP コードをコピペして試してみてください。

      • 早々にご返信いただきましてありがとうございます。
        PHPコードを書き換えますと、ページTOPに飛ぶ挙動は直りました。
        ありがとうございます。
        >h2のみ目次番号を残す(h3以下を消す)方法はありますでしょうか?
        こちらの質問方法がまずかったようでして、h3のタイトルは表示したまま、番号のみ消したいという意図でした。
        例としては以下の場合に2.1という数字のみ非表示にし、h3のテキストはそのまま残したいです。
        1 テキスト
        2 テキスト
        2.1 テキスト
        よろしくお願いいたします。

        • 下記のような CSS で h3 (h2 が1段目) 以降の番号を消すことができます。
          .toc ul ul .contentstable-number { display: none; }
          h4 以降なら、
          .toc ul ul ul .contentstable-number { display: none; }
          もし消えないようでしたら、!important を付けてみてください。

          • ご返信ありがとうございます。
            希望通りにすることができました。

  • 質問失礼致します。
    目次表示でh3にインデントがされず、全て左揃えで表示されてしまいます。
    もしかすると、リセットcssが原因かもしれませんが、どのプロパティかわかりません。
    原因わかりますでしょうか?

    • こんにちは、コメントありがとうございます。
      下記 CSS ではどうでしょうか?
      .toc ul ul { padding-left: 2rem; }
      反映されない場合は、!important を付与してみてください。
      .toc ul ul { padding-left: 2rem !important; }
      ※ 2rem は適当に変更してください。

      • ありがとうございます!
        解決いたしました!

        追加で質問なのですが、目次の開閉ボタンの角かっこ( [] )とテキストを削除し、画像を当てることは可能でしょうか?

        • 下記のような CSS で画像を当てることができます。
          .toc .toc-toggle {
          font-size: 0 !important;
          }
          .toc .toc-toggle .internal::before {
          content: '';
          display: inline-block;
          width: 28px;
          height: 28px;
          background-image: url(//example.com/画像.jpg);
          background-size: contain;
          vertical-align: top;
          }

  • はじめまして。
    こちらのコードを少し編集して使用させていただいております。
    ありがとうございます!
    こちらの内容を元に自分のブログに共有したいのですが、
    大丈夫でしょうか?

    • はじめまして、こんにちは。
      ライセンス表記のないコード(このページのコードなど)はご自由に利用してください。もちろん改変して配布しても問題ありません。CC0 ライセンスを選択されても問題ありません。ライセンス表記のあるコード(「Search Regex による Markdown 記法の一括置換」ページのコードなど)は、そのライセンスに従ってください。
      記事の文章や画像に関しては常識の範囲内でご利用ください。詳細は下記ページをご覧ください。
      https://xakuro.com/about/

  • はじめまして。
    すばらしいプログラムをありがとうございます。

    さて、貴コードを使って目次を出力したところ、本文に再利用ブロックが使われている場合には、その部分が目次リスト内に出力させないようです。

    HTMLソースを見ると、再利用ブロック内の見出しタグにidが付与されないことが原因かと思うのですが、idを付与し、リストに含める方法はありますでしょうか?

    • はじめまして、こんにちは。
      報告ありがとうございます。再利用ブロック内の見出しは盲点でした (^^;
      コードを修正(45行目の1行を追加)しましたので、試してみてください♪

  • テストしました。バッチリです!!
    ありがとうございます!!

    do_blocksで再解析できるんですね(知りませんでした)。
    勉強になりました。

    余談ですけど、あまり再利用ブロックって使われてないんですかね?

    • 再利用ブロックですか~
      便利なので使われていると思うのですが・・・そういう私はほとんど使ってませんが (^^ゞ
      どうなんでしょうかね?

  • こんにちは
    質問申し訳ありません。
    リンクを押して飛ぶ位置をtop100px等
    指定にすることは可能でしょうか?

    • はじめまして、こんにちは。
      パラメーターにはありません。m(__)m
      コード変更する場合はコードの157行目辺りを参照してみてください。

  • はじめまして。
    大変厚かましい質問で申し訳ありません。
    今はショートコードのコードに自動挿入コードを追加する形だと思うのですが、
    自動挿入コードのみで完結したコードがあれば助かるなぁと思いました。
    ぜひ検討頂けたらと思い、コメントさせていただきました。

    • はじめまして、こんにちは。
      すみません、自動挿入コードのみで完結したコードというのがよくわかりません。ショートコードを削除したいということでしょうか?もう少し具体的なご要望を教えて下さい。

      • テンプレートで使用するということでしょうか?テンプレートであれば、do_shortcode() を使用して表示することができます。

        例:

        echo do_shortcode( '[toc showcount="4"]' );
      • とんちんかんな事を言っていたら申し訳なのですが、
        ショートコードで目次を入れる機会が私にはほぼないので、
        自動挿入のみのコードも作っていただけらと思いました。
        私情でphpへの追記がかなり多く、少しでもコンパクトに出来たらと思いコメントさせて頂きました。

        • すみません、このコードからショートカット部分をカットしてもコンパクトにはなりません(数行増えることになりそう・・・)。

          • そうなんですね。
            お早い返信ありがとうございます!
            続けての質問で申し訳ないのですが、
            開閉するのに、タイトル部分をタップかクリックで開ける形にしたいのですが、
            どのようにしたらいいでしょうか?
            イメージ的には目次のタイトル背景部分のどの部分を押しても開閉できるようにしたいです。

          • 現行バージョンではそのような機能はありません。また今のところ追加する予定もありません。m(__)m

  • 設定がないのですね。
    無茶を言いまして失礼しました。
    丁寧に答えていただきありがとうございました。
    便利に使わせていただきます。

  • すいません、連続で質問失礼します;;
    既存の投稿にH2見出しをラストに追加した所
    最初のH2のIDがtoc2に変わり、
    ラストに追加したものはtoc11にならないといけない所、toc12となってしまいます。
    これによってアンカーリンクが↑に一つずつずれる現象が起きてます。
    原因は何かわかるでしょうか?

    • すみません、ちょっと状況がわかりません。
      実際のページの URL をお知らせください。

      • 返信ありがとうございます。
        今大幅リニューアル中につき、テスト環境下なので、URLに関してはご勘弁ください。

        通常見出しが有ればtocが順番に付くと思います。
        例えば
        h2見出し toc1
        h2見出し toc2
        といった具合にのものがあるとします。

        これを
        投稿済みのものを再編集して
        h2見出し
        h2見出し
        h2見出し←new

        とすると
        h2見出しtoc2
        h2見出しtoc3
        h2見出しtoc4

        といった具合に一つづつズレてid付与されているみたいです。

        目次のアンカー部分のURLは、#toc1から飛ぶように振られていて、追加の見出しも含めて正常に表記されています。

        しかし、本文中の見出しに振られたidが上記のようにtoc2から振られ始めているために
        目次からジャンプ先がズレてしまいます。

        説明下手ですいません。
        もし、何かわかりにくいことがあれば言ってください。

        • 検証してみましたが、現象を再現できませんでした。
          実際のページのソースの

          <div id="toc_content"></div>

          の中のすべてのヘッダータグ (h1~h6) を提示することはできますか?

          • 遅くまでお付き合い頂きありがとうございます。
            原因わかりました。
            最初のh2見出しより上の導入分にカードリンク(関連記事ブログカード)があり、そのタイトルにh4が当てられていて、それを拾ってしまっているみたいです。
            -例-
            はじめまして
            その内容についてはコチラを参照
            [ブログカードリンク 見出しタイトルh4]
            .
            .
            目次
            h2見出し1
            h2見出し2

            もし必要であればヘッダータグ内も記述しますが、明日になります、すいません。

            解決策とすれば、最初の見出しh2から順にid付与されるようになればいけそうな気もします。

            分かりにくい説明でごめんなさい。

          • なるほどわかりました (^^♪
            コードを修正 (バージョン4.0.0) したので、試してみてください。

  • 返信遅れましてすいません。
    ただいま修正 (バージョン4.0.0) を取り入れ確認したところ、見事に問題が解決しておりました。
    念の為、本文中にランダムでブログカードを挿入してみましたが、全て正常に目次が機能しています!
    夜分遅くにも関わらず、迅速かつ丁寧な対応に感謝いたします。

  • お世話になっております。
    もう一つ問題が発生したため、コメントさせていただきます。
    ブロックエディタにて「区切り線」を本文中にいれると、tocが飛ばされるようです。
    HTMLは

    です。

    例:
    目次
    h2(#toc1)
    h2(#toc2)
    h2(#toc3)

    h2タイトル(toc1)
    区切り線
    区切り線
    h2タイトル(toc4)
    区切り線
    区切り線
    h2タイトル(toc7)

    以上のようになり、目次が機能しなくなるみたいです。
    よろしくお願いします。

    • すみません。コードに不具合がありました。
      修正 (バージョン4.0.1) しましたので、試してみてください。

      • 確認した所、正常に動いているみたいです。
        迅速な対応ありがとうございました。
        助かりました!

  • はじめまして。
    すばらしいプログラムをありがとうございます。
    簡単に目次が作れて大変ありがたいです。
    厚かましくも質問させてください。

    ワードプレスのページ区切りを使うと、次のページの見出しに飛ばす1ページ目の最下部までスクロールしてしまいます。
    これを次ページの該当部分まで飛ばすことは可能でしょうか?
    ※こちら<!--nextpage-->でなく、ページ区切りブロックを利用しています。

    また見出しにショートコードを利用すると、そのショートコード自体も目次に表示されるようです。
    これを回避する事は可能でしょうか?
    ※[shorcode]コメント[/shorcode]

    • はじめまして、こんにちは。
      ショートコードの不具合およびページ分割(ページ区切り)に対応(バージョン4.1.0)しましたので、試してみてください。

      • 早速のご対応ありがとうございました!感激です。
        ページ分割は希望通り次のページへ遷移いたしました。

        ショートコードの方ですが、ショートコードは消えたのですが、ついでにショートコードに挟まれた本文も消えてしまい、つまり目次の該当部分が空白になってしまいました・・・
        ※[shorcode]コメント[/shorcode]のコメントも目次上では消えて見えない状況です。
        もしお時間ございましたらご確認頂けますとうれしいです。

        • 下記のように変更してみてください。
          131行目(抜粋):
          strip_shortcodes( $headers[3][$i] )

          wp_strip_all_tags( do_shortcode( $headers[3][$i] ), true )

          • ありがとうござました!完璧に表示されました。感謝感激でございます。大事に使わせて頂きます。

  • コメントにて失礼いたします。

    こちらの環境だけかも知れませんが、depthで階層数を指定しても、全ての見出しにidが付与されるようです。

    例えば[toc toplevel=”2″ depth=”1″]とした場合、目次側にはh2の見出しのみが出力されますが、記事コンテンツではh3やh4など、全ての見出しレベルにidが付与されます。

    そのため、下記のように目次から正しい見出し箇所にジャンプすることができなくなっています。

    ↓ ↓ ↓

    ▼目次
    <a href=”#toc1″>りんご(H2)</a>
    <a href=”#toc2″>みかん(H2)</a>
    <a href=”#toc3″>いちご(H2)</a>

    ▼記事コンテンツ側
    <h2 id=”toc1″>りんご(H2)</h2>
     <h3 id=”toc2″>青森(H3)</h3>

    <h2 id=”toc3″>みかん(H2)</h2>
     <h3 id=”toc4″>和歌山(H3)</h3>
      <h4 id=”toc5″>愛媛(H4)</h4>

    <h2 id=”toc6″>いちご(H2)</h2>
     <h3 id=”toc7″>山形(H3)</h3>

    PHPはそのままコピーして試しておりますが、そちらの環境では正常に出力、動作しておりますでしょうか?

    • こんにちは、コメントありがとうございます。
      PHP コードの不具合でした。(^^;
      修正(バージョン 4.1.1)しましたので、今一度お試しください。

        • お返事をいただきましてありがとうございます。
          修正バージョン(4.1.2)で試しましたところ、本件改善いたしました!
          非常に迅速なご対応に感謝いたします。

  • お世話になっております。
    再度、メールにて失礼いたします。

    ページ区切りで分割している場合、次ページの見出し箇所に飛ぶことは可能なのですが、前ページの見出し箇所には飛ぶことはできないようです。

    そこで、ページが分割されている場合は、目次項目も分割するといったことは可能でしょうか。
    ↓ ↓ ↓
    1ページ目には1ページ目の目次項目のみを出力
    2ページ目には2ページ目の目次項目のみを出力 …

    度々のご相談となり恐縮でございますが、ご確認をいただけますと幸いでございます。

      • 別ページの目次項目にクラス (other-page) を追加 (バージョン4.2.0) しました。このクラスを指定して非表示 (display: none) するのはどうでしょうか?
        CSS の例:

        .toc .toc-list .other-page {
        	display: none;
        }
        • お返事をいただきましてありがとうございます。
          ご提案いただいた方法で解決できそうです!

          今回も迅速にご対応をいただき大変助かりました。

  • 石鷹様
    はじめまして。一から自作は出来ず悩んでいたのですが素晴らしいプログラムをありがとうございます。
    初めから閉じた状態で使いたいのですが、

    「’close’ => false,」を「true」に変更すると、閉じた状態からにはなるのですが、開いても中身が「display: none;」のままで表示されない現象に悩んでいます。
    確認すると「toc-list」に下記のstyleがついていました。
    element.style {
    display: none;
    }
    デペロッパーツ―ルで削除すると正常に動くのですが、知識が乏しく何が原因なのか分かりませんでした。
    現在ローカル環境で実装中で、コードは全てそのままコピペしています。

    もしお時間がありましたら、なにかアドバイスいただけないでしょうか。
    よろしくお願いいたします。

    • はじめまして、こんにちは。
      すみません、PHP コードに不具合がありました。
      PHP コードを修正(バージョン 4.2.1)しましたので、再度お試しください。

      • お忙しいところ見ていただきありがとうございます。
        ちゃんと閉じた状態で表示されるようになりました。
        迅速に返信いただきありがとうございました!

popoppo へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。

日本語でコメントを入力してください。(スパム対策)