プラグインなし!目次を実装する方法。|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に「目次を実装する方法」をご紹介しました。

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

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

Web Wordpress セキュリティ プラグイン

WordPress データベースの接頭辞(プレフィックス)変更プラグイン「Change Table Prefix」

WordPressのセキュリティを強化したい場合は、データベーステーブルの接頭辞(プレフィックス)を変更する必要があります。 インストール後にシンプルに早く変更したい場合はこのプラグイン一択です。 C...

続きを見る

Web Wordpress コード セキュリティ

全ページ常時SSLにする方法。WordPress

WebサイトにSSLの導入はされていますでしょうか? SSL導入は、セキュリティ面でもSEO面でも優位になりますので、全Webサイトに推奨しています。 特にショッピングサイトやクラウドファンディングサ...

続きを見る

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

プラグインなし!内部・外部リンク対応のブログカードを表示する方法。|WordPress

WordPressで、プラグインを使わずにブログカードを表示する方法をご紹介致します。 プラグイン不使用には、サーバー負荷の軽減やサイト表示速度の保持など様々なメリットがあります。 WordPress...

続きを見る

Web Woocommerce Wordpress プラグイン

WordPressのユーザー情報をCSVエクスポート、インポートするプラグイン

「Import and export users and customers」というプラグインを利用します。 とにかくシンプルにユーザー情報をCSVでダウンロードできればいい、という方にお勧めです。 ...

続きを見る

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

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

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

3 コメント

続きを見る

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

Googleアドセンス(Google Adsense)広告を遅延読み込み(LazyLoad)して表示速度を高速化する方法。

Googleアドセンスの読み込みはページ表示速度を著しく低下させます。 以下方法で問題解決の期待値が向上しますので是非お試しください。 1. Google AdsenseコードのScriptタグを削除...

続きを見る

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

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

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

続きを見る

健康

長生きする方法

1. 喫煙しない2. 適度な運動3. 栄養バランスの良い食事4. 健康的な体重の維持5. 良質な睡眠6. ストレスをためないようにする7. まとめ 喫煙しない 長生きするための最善の方法はいくつかあり...

続きを見る

NGO ニュース

非政府団体「DAIKICI NGO」を設立しました。

本日、非政府団体「DAIKICI NGO」を設立しました。 地域、国内外にこだわらず地球全体の問題解決に取り組みながら、人類の成長、前進を目的としています。 目前には様々な問題、課題など山積みですが、...

続きを見る

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

WordPressのバックエンド投稿ページのフォントが小さい!フォントサイズ変更の方法。

WordPressのバックエンド投稿ページのフォントって小さくないですか? そんなときはこれ。 これだけ書いておけば投稿ページ(テキスト)のフォントサイズ等を自由にカスタマイズすることができます。 テ...

続きを見る

コメントを残す

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