記事中の見出しタグを使用し、自動で目次を作ってくれる便利なプラグイン「Table of Contents Plus」(以下 TOC+)をページ分割した記事でうまく動かしたい。
中にはプラグインを直接改造するとか書いてるサイトもあるけど、言うまでもないけどおすすめしないよ。
デフォルトの動作
以下のように <!--nextpage-->
を使用してページ分割した記事だとどう表示されるのかというと…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<h2>見出し1</h2> <p>文章1</p> <h2>見出し2</h2> <p>文章2</p> <!--nextpage--> <h2>見出し3</h2> <p>文章3</p> <h2>見出し4</h2> <p>文章4</p> <!--nextpage--> <h2>見出し5</h2> <p>文章5</p> <h2>見出し6</h2> <p>文章6</p> |
このように、分割後のページ単位で見出しが表示される。
全ページ分の見出しを出力する
これはこれでいいんだけど、そうではなく「全ページ分の見出しを出力する」ようにしたい。
今回は元のプラグイン自体には手を入れず、TOC+ の機能を使いつつ全ページ分出力するようなショートコードを作成する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
add_shortcode('allpage_toc', 'get_allpage_toc'); function get_allpage_toc( $content ) { global $post; if (preg_match('$<!--nextpage-->$', $post->post_content) && function_exists('toc_get_index')) { $pages = explode('<!--nextpage-->', $post->post_content); $toc = ''; foreach ($pages as $key => $page) { // 対象のページ番号 $page_num = $key + 1; $page_content = toc_get_index($page); $page_permalink = get_permalink($post->ID); // ページ番号付加後のURL $page_permalink_paged = ''; if ($page_num == 1) { $page_permalink_paged = $page_permalink; } else { if (mb_substr($page_permalink, -1) == "/") { $page_permalink_paged = $page_permalink . $page_num . '/'; } else { $page_permalink_paged = $page_permalink . '/' . $page_num; } } $index = preg_replace( "!#!", $page_permalink_paged . '#', $page_content ); if ($index != "") { $class = "pageNo"; // 現在のページ番号 $paged = (get_query_var('page')) ? get_query_var('page') : 1; if ($page_num == $paged) { $class .= " currentpage"; $index = "<li class='$class'>Page $page_num</li>" . $index; } else { $index = "<li class='$class'><a href='$page_permalink_paged'>Page $page_num</a></li>" . $index; } } $toc .= $index; } global $tic; $toc_options = $tic->get_options(); $style = '<style>div#toc_container:not(.paging_toc) {display: none !important}</style>'; $tic->enable(); return $style . '<div id="toc_container" class="paging_toc"><p class="toc_title paging_toc">'. $toc_options['heading_text'] . '</p><ul class="toc_list">'.$toc .'</ul></div><!--TOC-->'. $content; } else { return $content; } } |
- TOC+ で作成された目次も出力されてしまうので、そっちだけを非表示にするCSSも出力している。
- 出力形式のカスタマイズは、
toc_get_index(content)
を呼ぶ前に$tic->set_option(array)
で設定することで可能になる。TOC+のソースを参考にしよう。 - (2021/6/29追記) バージョン 2106 以降では、
$tic
となっている部分を$toc_plus
に変更すること。(変数名が変えられているため)
そうしたら、作成したショートコードを出力したい部分に記述する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[allpage_toc] <h2>見出し1</h2> <p>文章1</p> <h2>見出し2</h2> <p>文章2</p> <!--nextpage--> [allpage_toc] <h2>見出し3</h2> <p>文章3</p> <h2>見出し4</h2> <p>文章4</p> <!--nextpage--> [allpage_toc] <h2>見出し5</h2> <p>文章5</p> <h2>見出し6</h2> <p>文章6</p> |
以下のように表示される。
使用例
実際に使っているのがこちら(※上記のソースより更にカスタマイズを入れて丁寧に作ってるけど基本的な作り方は同じ)
注意点
TOC+ が有効でないと見出しタグにアンカーが作成されず、正常にジャンプできなくなる。つまり「目次を出すため」ではなく「目次から正しい位置に飛ぶため」に、リンク先のページでも TOC+ の有効化が必要となる。 [no_toc]
で TOC+ を OFF にしないこと。
逆に「1ページ目だけ目次を出して2ページ目以降には出したくない」などという場合はひと工夫する必要がある。たとえば2ページ目以降は [allpage_toc]
を記述せず、かつ TOC+ で出力された目次を消すCSSを仕込むなど。
バージョン2402 以降の対応
Table of Contents Plus バージョン2402 でプラグイン内部の処理が変更されたため、このページに書いてある方法では、目次は作成されますが見出しにアンカーがつかず、正常にジャンプできなくなりました。
2402
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public function is_eligible() { global $post; $custom_toc_position = strpos( $post->content, '[TOC]' ); if ( ! $this->options['rest_toc_output'] ) { if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return false; } } if ( is_feed() ) { return false; } if ( false !== $custom_toc_position ) { ... |
2406
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public function is_eligible() { global $post; $custom_toc_position = isset( $post->post_content ) ? has_shortcode( $post->post_content, 'toc' ) : false; if ( ! $this->options['rest_toc_output'] ) { if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return false; } } if ( is_feed() ) { return false; } if ( false !== $custom_toc_position ) { ... |
有効にするかどうかを、本文中に [toc]
ショートコードがあるかどうかで判断するようになった。
とりあえずショートコードを本文に書けば動くし、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[allpage_toc] [toc] <h2>見出し1</h2> <p>文章1</p> <h2>見出し2</h2> <p>文章2</p> <!--nextpage--> [allpage_toc] [toc] <h2>見出し3</h2> <p>文章3</p> <h2>見出し4</h2> <p>文章4</p> <!--nextpage--> [allpage_toc] [toc] <h2>見出し5</h2> <p>文章5</p> <h2>見出し6</h2> <p>文章6</p> |
それかプラグインを直接いじるか…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public function is_eligible() { global $post; $hastoc = has_shortcode( $post->post_content, 'toc' ) || has_shortcode( $post->post_content, 'allpage_toc' ); $custom_toc_position = isset( $post->post_content ) ? $hastoc : false; if ( ! $this->options['rest_toc_output'] ) { if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { return false; } } if ( is_feed() ) { return false; } if ( false !== $custom_toc_position ) { … |
でもこんなのやりたくないよね。
結局、TOC+ の is_eligible()
より先に $post->post_content
の中身を書き換えるようにすればいい…。
これが呼ばれているのは以下の処理の中なので、
1 |
add_filter( 'the_content', [ $this, 'the_content' ], 100 ); |
優先順をとりあえず1つ前の99とかに設定して、中身を強制的に書き換える。具体的には今回作成したショートコードの前とかに強制的に [toc]
を足してやって is_eligible()
の判定をすり抜けてしまえばいい。
1 2 3 4 5 6 7 8 9 10 |
add_filter( 'the_content', 'allpage_toc_handler', 99 ); function allpage_toc_handler( $content ) { global $post; if ( isset( $post->post_content ) ) { if ( false !== has_shortcode( $post->post_content, 'allpage_toc' ) ) { $post->post_content = str_replace('[allpage_toc', '[toc] [allpage_toc', $post->post_content); } } return $content; } |
これでアンカーが正常に動くようになるので以前と同じ動作に戻せる。