プラグインなし!目次を実装する方法。|WordPress

WordPressで、プラグインを使わずに目次を実装する方法をご紹介します。

JQueryを使わない方法で、本サイトでも導入している実装方法になります。

時間もかからず工数も少ないので、宜しければご参考にされてください。

次の手順で実装していきます。

  1. functions.phpの編集
  2. CSSの編集
  3. 外観→カスタマイズ画面で表示条件の設定
  4. 個別記事内で表示・非表示の調整

functions.phpの編集

functions.phpの編集を行います。

子テーマが利用できる環境であれば子テーマのfunctions.phpを利用してください。

各コードの説明書きはそれぞれコード上部に記載していますので、参考にされてください。

※必ずバックアップをとってから編集してください。

//////////////////////////////////////////////////
//Original sanitize_callback
//////////////////////////////////////////////////
// CheckBox
function fit_sanitize_checkbox( $checked ) {
    return ( ( isset( $checked ) && true == $checked ) ? true : false );
}
// radio/select
function fit_sanitize_select( $input, $setting ) {
	$input = sanitize_key( $input );
    $choices = $setting->manager->get_control($setting->id)->choices;
    return ( array_key_exists( $input, $choices ) ? $input : $setting->default );
}
// number limit
function fit_sanitize_number_range( $number, $setting ) {
    $number = absint( $number );
    $atts = $setting->manager->get_control( $setting->id )->input_attrs;
    $min = ( isset( $atts['min'] ) ? $atts['min'] : $number );
    $max = ( isset( $atts['max'] ) ? $atts['max'] : $number );
    $step = ( isset( $atts['step'] ) ? $atts['step'] : 1 );
    return ( $min <= $number && $number <= $max && is_int( $number / $step ) ? $number : $setting->default );
}


  
//////////////////////////////////////////////////
//投稿ページ各種設定画面
//////////////////////////////////////////////////
function fit_post_cutomizer( $wp_customize ) {
// セクション
	$wp_customize->add_section( 'fit_post_section', array(
		'title'     => '投稿ページ設定',
		'priority'  => 1,
	));
  
	
	
	
	// 目次の表示/非表示 セッティング
	$wp_customize->add_setting( 'fit_post_outline', array(
		'default'   => 'value1',
		'type' => 'option',
		'sanitize_callback' => 'fit_sanitize_select',
	));
	// 目次の表示/非表示 コントロール
	$wp_customize->add_control( 'fit_post_outline', array(
		'section'   => 'fit_post_section',
		'settings'  => 'fit_post_outline',
		'label'     => '■目次の表示/非表示',
		'description' => '投稿ページに目次を表示するか選択<br>
		(記事内の最初のhタグの手前に自動で挿入されます。※
		ショートコードで好きな位置に表示可能)',
		'type'      => 'select',
		'choices'   => array(
			'value1' => '表示する(default)',
			'value2' => '表示しない',
		),
	));
	
	// 目次を表示するための最小見出し数 セッティング
	$wp_customize->add_setting( 'fit_post_outline_number', array(
		'default'   => '1',
		'type' => 'option',
		'sanitize_callback' => 'fit_sanitize_number_range',
	));
	// 目次を表示するための最小見出し数 コントロール
	$wp_customize->add_control( 'fit_post_outline_number', array(
		'section'   => 'fit_post_section',
		'settings'  => 'fit_post_outline_number',
		'description' => '目次を表示するための最小見出し数を指定',
		'type'      => 'number',
		'input_attrs' => array(
        	'step'     => '1',
        	'min'      => '1',
        	'max'      => '50',
    	),
	));

	// 目次パネルデフォルト設定 セッティング
	$wp_customize->add_setting('fit_post_outline_close', array( 
		'type' => 'option',
		'sanitize_callback' => 'fit_sanitize_checkbox',
    ));
	// 目次パネルデフォルト設定 コントロール
	$wp_customize->add_control('fit_post_outline_close', array( 
        'section' => 'fit_post_section', 
        'settings' => 'fit_post_outline_close', 
        'label'     => '目次パネルをデフォルトで閉じておく',
        'type'      => 'checkbox',
    ));
	
	
	

}
add_action( 'customize_register', 'fit_post_cutomizer' );


//////////////////////////////////////////////////
//目次の表示/非表示、個別選択設定
//////////////////////////////////////////////////
if ( get_option('fit_post_outline') != 'value2') {
	function add_outline_fields() {
		//add_meta_box(表示される入力ボックスのHTMLのID, ラベル, 表示する内容を作成する関数名, 投稿タイプ, 表示方法)
		add_meta_box( 'outline_setting', '目次の個別非表示設定', 'insert_outline_fields', 'post', 'normal');
	}
	add_action('admin_menu', 'add_outline_fields');
 
 
	// カスタムフィールドの入力エリア
	function insert_outline_fields() {
		global $post;
	
		if( get_post_meta($post->ID,'outline_none',true) == "1" ) {
			$outline_none_check = "checked";
		}else {
			$outline_none_check = "";
		}
	
		echo '
			<div style="margin:20px 0; overflow: hidden; line-height:2;">
		  	<div style="float:left;width:120px;">目次の表示設定</div>
		  	<div style="float:right;width:calc(100% - 140px);">
		    	<input type="checkbox" name="outline_none" value="1" '.$outline_none_check.' >:この投稿では目次を非表示にしますか?
		  	</div>
		  	<div style="clear:both;"></div>
			</div>
		';
	
	}

	// カスタムフィールドの値を保存
	function save_outline_fields( $post_id ) {
		if(!empty($_POST['outline_none'])){
			update_post_meta($post_id, 'outline_none', $_POST['outline_none'] );
		}else{
			delete_post_meta($post_id, 'outline_none');
		}

	}
	add_action('save_post', 'save_outline_fields');
}


//////////////////////////////////////////////////
//オリジナル目次を作成
//////////////////////////////////////////////////
function get_outline_info($content) {
	// 目次のHTMLを入れる変数を定義します。
	$outline = '';
	// h1?h6タグの個数を入れる変数を定義します。
	$counter = 0;
    // 記事内のh1?h6タグを検索します。(idやclass属性も含むように改良)
    if (preg_match_all('/<h([1-4])[^>]*>(.*?)<\/h\1>/', $content, $matches,  PREG_SET_ORDER)) {
    	   // 記事内で使われているh1?h6タグの中の、1?6の中の一番小さな数字を取得します。
    	   // ※以降ソースの中にある、levelという単語は1?6のことを表します。
        $min_level = min(array_map(function($m) { return $m[1]; }, $matches));
        // スタート時のlevelを決定します。
        // ※このレベルが上がる毎に、<ul></li>タグが追加されていきます。
        $current_level = $min_level - 1;
        // 各レベルの出現数を格納する配列を定義します。
        $sub_levels = array('1' => 0, '2' => 0, '3' => 0, '4' => 0);
        // 記事内で見つかった、hタグの数だけループします。
        foreach ($matches as $m) {
            $level = $m[1];  // 見つかったhタグのlevelを取得します。
            $text = $m[2];  // 見つかったhタグの、タグの中身を取得します。
            // li, ulタグを閉じる処理です。2ループ目以降に中に入る可能性があります。
            // 例えば、前回処理したのがh3タグで、今回出現したのがh2タグの場合、
            // h3タグ用のulを閉じて、h2タグに備えます。
            while ($current_level > $level) {
                $current_level--;
                $outline .= '</li></ul>';
            }
            // 同じlevelの場合、liタグを閉じ、新しく開きます。
            if ($current_level == $level) {
                $outline .= '</li><li class="outline__item">';
            } else {
                // 同じlevelでない場合は、ul, liタグを追加していきます。
                // 例えば、前回処理したのがh2タグで、今回出現したのがh3タグの場合、
                // h3タグのためにulを追加します。
                while ($current_level < $level) {
                    $current_level++;
                    $outline .= sprintf('<ul class="outline__list outline__list-%s"><li class="outline__item">', $current_level);
                }
                // 見出しのレベルが変わった場合は、現在のレベル以下の出現回数をリセットします。
                for ($idx = $current_level + 0; $idx < count($sub_levels); $idx++) {
                    $sub_levels[$idx] = 0;
                }
            }
            // 各レベルの出現数を格納する配列を更新します。
            $sub_levels[$current_level]++;
            // 現在処理中のhタグの、パスを入れる配列を定義します。
            // 例えば、h2 -> h3 -> h3タグと進んでいる場合は、
            // level_fullpathはarray(1, 2)のようになります。
            // ※level_fullpath[0]の1は、1番目のh2タグの直下に入っていることを表します。
            // ※level_fullpath[1]の2は、2番目のh3を表します。
            $level_fullpath = array();
            for ($idx = $min_level; $idx <= $level; $idx++) {
                $level_fullpath[] = $sub_levels[$idx];
            }
            $target_anchor = 'outline__' . implode('_', $level_fullpath);

            // 目次に、<a href="#outline_1_2">1.2 見出し</a>のような形式で見出しを追加します。
            $outline .= sprintf('<a class="outline__link" href="#%s"><span class="outline__number" style="display:none;">%s.</span> %s</a>', $target_anchor, implode('.', $level_fullpath), strip_tags($text));
            // 本文中の見出し本体を、<h3>見出し</h3>を<h3 id="outline_1_2">見出し</h3>
            // のような形式で置き換えます。
            $hid = preg_replace('/<h([1-6])/', '<h\1 id="' .$target_anchor . '"', $m[0]);
            $content = str_replace($m[0], $hid, $content);
			
        }
        // hタグのループが終了後、閉じられていないulタグを閉じていきます。
        while ($current_level >= $min_level) {
            $outline .= '</li></ul>';
            $current_level--;
        }
        // h1?h6タグの個数
        $counter = count($matches);
    }
    return array('content' => $content, 'outline' => $outline, 'count' => $counter);
}

//目次を作成します。
function add_outline($content) {

    // 目次を表示するために必要な見出しの数
	if(get_option('fit_post_outline_number')){
		$number = get_option('fit_post_outline_number');
	}else{
		$number = 1;
	}
    // 目次関連の情報を取得します。
    $outline_info = get_outline_info($content);
    $content = $outline_info['content'];
    $outline = $outline_info['outline'];
    $count = $outline_info['count'];
	if (get_option('fit_post_outline_close') ) {
		$close = "";
	}else{
		$close = "checked";
	}
    if ($outline != '' && $count >= $number) {
        // 目次を装飾します。
        $decorated_outline = sprintf('
		<div class="outline">
		  <span class="outline__title">目次</span>
		  <input class="outline__toggle" id="outline__toggle" type="checkbox" '.$close.'>
		  <label class="outline__switch" for="outline__toggle"></label>
		  %s
		</div>', $outline);
        // カスタマイザーで目次を非表示にする以外が選択された時&個別非表示が1以外の時に目次を追加します。
		if ( get_option('fit_post_outline') != 'value2' && get_post_meta(get_the_ID(), 'outline_none', true) != '1' && is_single() ) {
        	$shortcode_outline = '
		';
        	if (strpos($content, $shortcode_outline) !== false) {
            	// 記事内にショートコードがある場合、ショートコードを目次で置換します。
            	$content = str_replace($shortcode_outline, $decorated_outline, $content);
        	} else if (preg_match('/<h[1-6].*>/', $content, $matches, PREG_OFFSET_CAPTURE)) {
            	// 最初のhタグの前に目次を追加します。
            	$pos = $matches[0][1];
            	$content = substr($content, 0, $pos) . $decorated_outline . substr($content, $pos);
        	}
		}
    }
	return $content;
}
add_filter('the_content', 'add_outline');

function override_mce_options( $init_array ) {
    global $allowedposttags;

    $init_array['valid_elements']          = '*[*]';
    $init_array['extended_valid_elements'] = '*[*]';
    $init_array['valid_children']          = '+a[' . implode( '|', array_keys( $allowedposttags ) ) . ']';
    $init_array['indent']                  = true;
    $init_array['wpautop']                 = false;

    return $init_array;
}

add_filter( 'tiny_mce_before_init', 'override_mce_options' );

CSSコードの編集

CSSの調整を行います。

CSSコードは以下になります。

style.cssなど任意のスタイルシートに丸ごとコピペしてください。

/*目次*/
.outline{
	border:1px dotted #D8D8D8;
	padding:20px;
	margin-top:20px;
	display:inline-block;
	font-size:0.9em;
	line-height:1.5em;
}
.outline__toggle{display: none;}
.outline__switch::before{
	content:"開く";
	cursor:pointer;
	border: solid 1px #D8D8D8;
	padding:5px;
	font-size:0.8rem;
	margin-left:5px;
	border-radius: 5px;
}
.outline__toggle:checked + .outline__switch::before{content:"閉じる"}
.outline__switch + .outline__list{
	overflow:hidden;
	width:0;
	height:0;
	margin-top:0;
	margin-left:-20px;
	transition: 0.2s;
}

.outline__toggle:checked + .outline__switch + .outline__list {
    width: auto;
    height: auto;
    margin-top: 20px;
    transition: 0.2s;
    border-top: dotted 1px #d2d2d2;
    padding-top: 1em;
}
.outline__item:before {content: normal;}
.outline__link{
	display:relative;
	color:#191919 !important;
}
.outline__link:hover{border:none;}
.outline__number{
	display: inline-block;
	color:#7F7F7F;
	background:#F2F2F2;
	padding:3px 6px;
	font-weight:400;
	font-size:1.2rem;
	margin-right: 5px;
}
label.outline__switch {
    position: relative;
    float: right;
}
li .outline__item{
list-style-type:none!important;
}
li .outline__item:before{
content:'- ';
}
ul .outline__item{
	list-style-type:none!important;
}
ul{
		-webkit-padding-start: 1.2em;
}

CSSをカスタマイズすることで任意のデザインに変更可能です。

CSSの編集が完了したら、テーマで使用しているCSSに保存してください。

設定については以上で完了です。

外観→カスタマイズ画面で表示条件の設定

「外観」→「カスタマイズ」→「投稿ページ設定」に進みます。

投稿ページで目次を表示するか選択

標準で目次を表示するかしないかです。

この項目は基本的に「表示」でよいと思います。

たまにしか目次が必要ではないという場合は「非表示」でよいと思います。

目次を表示するための最小見出し数を設定

見出しの数が何個あったら目次を表示するかです。

3~5くらいが標準的な数だと思います。仮に3にした場合は1個か2個しか見出しがないときは目次は生成されません。

当サイトでは2に設定しています。

目次パネルをデフォルトで閉じておく

目次欄をデフォルトで最初から閉じた状態にするかです。

この項目はチェックをしない状態でよいと思います。

個別記事内で表示・非表示の調整

基本的に操作することはほとんどないのですが、見出しの数が多く標準で目次が表示されてしまうものの投稿内のデザイン・見た目の関係上非表示にしたいことがあります。

そういう場合は個別記事内にチェックボックスがありますのでチェックを入れてください。

まとめ

今回はプラグインを使わずにWordPressに「目次を実装する方法」をご紹介しました。

作業自体は時間もかからず、すぐに実装できますので宜しければ参考にされてください。

最後までご覧いただきありがとうございました。

植物

アガベ ビクトリア レジーナ 笹の雪 Agave victoriae-reginae ‘Sasanoyuki’

1. 基本情報1.1. 学名1.2. 和名1.3. 科名1.4. 属名1.5. 種名1.6. 原産国1.7. 自生地1.8. 形態1.9. 成木1.10. 耐寒性1.11. 耐暑性1.12. 日照1....

続きを見る

Web Wordpress コード スピード

コンタクトフォーム7(Contact Form 7)指定ページのみjsとcssを読み込ませる方法。|WordPress

WordPressで、プラグインを使わずにContactForm7のjsとcssを指定ページのみ読み込ませる方法をご紹介します。 通常ContactForm7を導入すると、すべてのページでjsとcss...

続きを見る

植物

Copiapoa cinerea var. cinerea コピアポア シネレア 黒王丸

黒王丸 コピアポア・シネレア Copiapoa cinerea は、「白サボテン」と呼ばれ日本で最も人気の高いサボテンと言っても過言ではありません。 肌が特に白く、真っ黒のトゲの個体は高値で取引されて...

続きを見る

アニメ

2024年春アニメ放送後人気ランキング。

1. 【第10位】変人のサラダボウル2. 【第9位】夜のクラゲは泳げない3. 【第8位】ザ・ファブル4. 【第7位】忘却バッテリー5. 【第6位】この素晴らしい世界に祝福を!36. 【第5位】終末トレ...

続きを見る

Web Wordpress コード スピード プラグインなし

プラグインなし!シンタックスハイライトPrism.jsの使い方|WordPress

今回はWordPressに、プラグインを使わずにシンタックスハイライトを実装する方法をご紹介します。 Webサイトやブログサイトなどにソースコードを表示させる場合に、シンタックスハイライトを使うことが...

3 コメント

続きを見る

Web Woocommerce Wordpress コード プラグインなし

Woocommerceの属性に「親」と「子」を割り当てたい!プラグインなしで可能にする方法。

Woocommerceの「属性」にカテゴリーのように「親」と「子」を作成できればいいと思ったことはありませんか? かつてはこの機能がありましたがバージョンアップを経てなくなってしまいました。 現在のバ...

続きを見る

Web Wordpress コード プラグインなし

プラグインなし!簡単コピペ。WordPress にサイト名のショートコードを追加する方法。

サイトを複数制作する際にサイト名を書き換えるのは非常に手間です。 これを未然に防ぐ方法としてショートコードでサイト名を呼び出すシステムを組んでおくことをおすすめします。 「functions.php」...

続きを見る

ショップ 入荷情報

2024年12月入荷予定

12月も続々登場します! 予約販売も承っております。数に限りがございますのでご希望の場合はお早めにご注文いただけますと幸いです。

続きを見る

植物

サボテンとは。「Cactos」の由来と特徴【1】

「Cactus」という言葉はギリシャ語の「Kàktos」に由来します。 この言葉はテオフラストス(Theophrastus)の著書「HistoriaPlantarum」で「未知の棘のある植物」を表すた...

続きを見る

Web YouTube コード プラグインなし

CSSでYouTubeの埋め込みを比率維持したままレスポンシブ対応させる方法。

YouTubeの埋め込みを比率維持したままレスポンシブ対応させる方法をご紹介します。 1. 手順1:divタグ追加2. 手順2:css追加3. 完成 手順1:divタグ追加 iframeタグを囲うよう...

続きを見る

コメントを残す

本サイトは、皆様に快適な閲覧をご提供させていただくためにcookieを使用しています。cookieの使用に同意しますか?