テンプレート

【WordPress】自作の目次の作り方 (プラグインなし/コピペコードあり)

この記事は最終更新から2年以上経過しており、内容が古い可能性があります

[mill]

今回は、見出しをもとにリンク付きの目次を自動生成するコードのご案内です。

2020-11-21
phpバージョン変更で一部にエラーが出たため phpコードを一部修正しています m(__)m

SEO的にも、閲覧者の利便性にも、ページ内リンクって大切。
今まで、jQueryで作った目次を使ったり、特急の時は有名どころの目次プラグインにお世話になったりしていましたが、、、

ふと気が付きました。。。
jQueryの目次、ちゃんと動いてはいるけど、ソースに出ないしSEOとしてはどうなんだろう??
ずーっと更新されてないプラグイン。いつまで使える? 大丈夫???

という事で、サイトのリニューアルに合わせて、プラグインなしphpで動く目次コードを作っちゃいました。これで、しばらくはいけるかな~(*’▽’)ww

このページの目次
  1. 目次の作り方
    1. phpコード(コピペOK)
      1. さらっとご説明
    2. スタイルシート
      1. 本体部分
      2. CSSでの開閉
      3. 開閉方法を簡単にご説明

目次の作り方

この上にあるのが、以下のコードで作った当サイトの目次です^^v

functions.php に直接貼ってもいいのですが、管理のしやすさを考えると別ファイルにしてfunctions.php に読込せるのがお勧めです。(別ファイル化はいつか、、投稿するかもw)

直接貼る場合は、1行目を削除してくださいm(__)m

phpコード(コピペOK)

2020-11-21 phpコード修正済

<?php // ← functions.phpに直接貼る場合は、この行を削除!!
function my_add_content( $content ) { 
if ( is_single() && in_the_loop() && is_main_query() ) { 
	$pattern = '/<h[2-5]>(.*?)</h[2-5]>/i'; //← h2~h5 に対応させる
	preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER ); 
	
	if( count( $matches ) > 1 ){  
		$toc = '<ol class="postin-list">'. PHP_EOL; 
		$hierarchy = NULL; 
		$i = 0; 
		
		foreach( $matches as $element ){  
			$i++; 
			$id = 'toc' . $i; 
			$chapter = preg_replace( '/<(.+?)>(.+?)</(.+?)>/',  '<$1 id="' . $id . '">$2</$3>', $element[0] ); 
			$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;} 
			elseif( strpos( $element[0], '<h5' ) === 0 ){$level = 3;} 
			$hclass = $level + 2; 
   
			if( $hierarchy === $level ){ $toc .= '</li>'. PHP_EOL; $hierarchy = $level; } 
			elseif( $hierarchy < $level ){ $toc .= PHP_EOL .'<ol class="toc-h'. $hclass .'">'. PHP_EOL; $hierarchy = $level; } 
			elseif( $hierarchy > $level ){ 
				$closing = str_repeat('</li>'. PHP_EOL .'</ol>', $hierarchy - $level );
				$toc .= $closing . PHP_EOL .'</li>'. PHP_EOL; $hierarchy = $level; }
			elseif( $i == 1 ){ $hierarchy = 0; } 
 
			$title = $element[1];  
			$toc .= '<li><a href="#' . $id . '">' . $title . '</a>'; 
		} 
		$toc .= str_repeat('</li>'. PHP_EOL .'</ol>', $level + 1);  
 
		$index = '<nav class="postin-nav toc"><p class="postin-tti">このページの目次</p> 
<input type="checkbox" id="toc-ch" name="toc-ch" class="postin-ch" checked><label for="toc-ch" class="postin-label"></label>'. PHP_EOL . $toc . '</nav>'. PHP_EOL; 
 
		$h2 = '/<h2.*?>/i'; 
		if (preg_match($h2, $content, $h2s)) { 
			$content = preg_replace($h2, $index . $h2s[0], $content, 1); 
		} 
	} 
} 
return $content; } 
add_filter( 'the_content', 'my_add_content' ); 

さらっとご説明

7行目 見出し2個以上で表示 ⇒ 表示されにくくするなら数字を増やします
12~33行目 繰返しであれこれ処理
 
13~16 ⇒ 投稿内の見出しに [id=”toc○”] を付与
18~22 ⇒ リストの下準備
24~29 ⇒ リストの中身
[前行の階層=$hierarchy]と[その行の階層=$level]を比べて表示するタグを指定。
子<ol>のclass=toc-ch*(配下liが対応する見出し)を変える時は 25行目。
32 <li>行の書き出し( </li>の階層対応は 27、28行目で指定 )
34~37行目 [ラッパー&見出し] を含む最終的な書き出し設定
 
34行目 ⇒ 階層計算した最後の閉じタグ
36~37 ⇒ css開閉用のチェックボックスとラベル(ピンク色部分) と、最終書き出し設定
checked ページを表示した際に閉じているようにします。開いた状態から始めたい場合は削除してください。
39~42行目 表示位置 (最初の<h2> の上)

開閉不要の場合は 37行目のピンク部分

<input type="checkbox" id="toc-ch" name="toc-ch" class="postin-ch" checked><label for="toc-ch" class="postin-label"></label>

を削除します。

スタイルシート

本体部分

※ 当サイトでは 行頭アイコンにFontAwesomeを使用していますが、以下のソースでは、使いやすいよう »(&raquo;)にしてあります。

.postin-nav {
	position: relative; 
	overflow: hidden;
	font-size: 14px; 
	padding: 0; 
	margin: 1rem 4%; 
	border: solid 1px #ccc; 
	border-radius: 1rem; } 
.postin-tti {
	background: #f6f6f6; 
	font-weight: bold; 
	line-height: 2.2em; 
	padding: 0 7em 0 1rem; 
	margin: 0; 
	border-radius: 1rem 1rem 0 0; } 
.postin-nav ol {
	list-style: none; margin: 5px 0; } 
.postin-nav ol.postin-list {
	border-radius: 0 0 1rem 1rem; } 
.postin-nav li {
	position: relative; 
	line-height: 1.4; 
	padding: 5px 0.5em 3px 1.8em; 
	margin: 0; } 
.postin-list > li {
	order-top: dotted 1px #bbb; } 
.postin-list > li li {
	border-top: 0; } 
.postin-nav li:before {
	position: absolute; top: 2px; left: 1rem; 
	content: "»"; 
	color: #ccc; 
	padding-right: 7px; } 

CSSでの開閉

※開閉不要なら以下は必要ありません^^

input.postin-ch {
	position: absolute; top: 0; right: 0; 
	z-index: 1; 
	opacity: 0; }
label.postin-label {
	position: absolute; top: 0; right: 7px; 
	display: block; 
	width: 100%; 
	z-index: 100; 
	font-size: 1em; line-height: 2.2; text-align: right; 
	margin: 0; 
	cursor: pointer; }
label.postin-label:before {
	content: "▲Close"; 
	display: inline-block; 
	background: #343434; 
	color: #fff; font-weight: bold; line-height: 1.6; 
	padding: 0 7px; 
	border-radius: 10px; }
input.postin-ch + label.postin-label + ol.postin-list {
	max-height: 40vh; 
	overflow-y: auto; 
	transition: transform 0.3s ease-out; }
input.postin-ch:checked + label.postin-label:before {
	content: "▼Open"; } 
input.postin-ch:checked + label.postin-label + ol.postin-list { 
	max-height: 0; 
	margin: 0; }

開閉方法を簡単にご説明

checkboxのチェックの有無で開閉させます。(チェック時は閉じる)

  • checkbox ⇒「opacity: 0;」→ 透過させて見えないように
  • label ⇒「width: 100%;」→ タイトル行クリックで反応
  • ol.postin-list ⇒ max-height: 40vh; で高さ指定

※max-height で強引にtransitionさせているので、内容が少ない場合、開閉時に多少カク付きがでます _ _;;