Gutenberg ブロックで書かれた記事対応の新しい目次作成記事があります。
このブログはまだはてなブログですが、WordPress に移行したサイトがあり、プラグインなしで目次を自動作成、自動表示する機能を作成しました。
- WordPress サイト「そんなには褒めないよ。映画評」
- 投稿の新規追加、更新時に目次を作成する
- 目次は h2, h3 要素から作成する(h4〜も可)
- 目次はカスタムフィールドに保存する
- 目次の表示は投稿ページ表示時にカスタムフィールドを呼び出す
- すでに目次が挿入されている記事があることを想定する
01functions.php
ご利用になる場合は、iframe など空要素になるタグで問題があります。次の記事をお読みください。
アクションフック save_post を使って、記事の投稿、更新時に目次を作成してカスタムフィールドに保存します。
/* 目次作成 登録、更新時にカスタムフィールド toc に保存する 記事内に {toc} を書いておくと single.php から呼び出される */ add_action( 'save_post', 'create_table_of_contents', 10, 3 ); function create_table_of_contents( $post_ID, $post, $update ) { // class-wp-widget-text.php のコードをを利用 $doc = new DOMDocument(); // Suppress warnings generated by loadHTML. $errors = libxml_use_internal_errors( true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @$doc->loadHTML( sprintf( '<!DOCTYPE html><html><head><meta charset="%s"></head><body>%s</body></html>', esc_attr( get_bloginfo( 'charset' ) ), $post->post_content ) ); libxml_use_internal_errors( $errors ); // ここまでclass-wp-widget-text.php のコードをを利用 $dom = new DOMXPath($doc); $children = $dom->query( '//body/*' ); $toc = '<ul class="table-of-contents">'; $h2 = false; $h3 = false; foreach($children as $child){ $tag = $child->tagName; if($tag == 'h2' && !$h2){ $id = $child->nodeValue; $child->setAttribute('id', $id); $toc .= '<li><a href="#' . $id . '">' . $child->nodeValue . '</a>'; $h2 = true; }else if($tag == 'h3'){ if(!$h3){ $toc .= '<ul>'; $h3 = true; } $id = $child->nodeValue; $child->setAttribute('id', $id); $toc .= '<li><a href="#' . $id . '">' . $child->nodeValue . '</a></li>'; }else if($tag == 'h2' && $h2){ if($h3){ $toc .= '</ul>'; $h3 = false; } $id = $child->nodeValue; $child->setAttribute('id', $id); $toc .= '</li><li><a href="#' . $id . '">' . $child->nodeValue . '</a>'; } } if($h3){ $toc .= '</ul>'; } $toc .= '</li></ul>'; $html = $doc->saveXML(); preg_match('/<body>(.*)<\/body>/is', $html, $matches); $post->post_content = $matches[1]; remove_action('save_post','create_table_of_contents', 10, 3); wp_update_post($post); add_action('save_post','create_table_of_contents', 10, 3); $result = add_post_meta($post_ID, 'toc', $toc, true); if(!$result){ update_post_meta($post_ID, 'toc', $toc); } }
アクションフック save_post は投稿が保存された直後に実行されますので、保存されたデータの h2, h3 要素を DOMDocument と DOMXPath を使ってパースし目次用のリストを作成します。同時に、h2, h3 要素に要素の内容を id として追加し、目次からのページ内リンクを貼ります。
h2, h3 要素の id を追加していますので投稿内容を再保存する必要があります。ただ、そのまま wp_update_post($post);
としますと無限ループに陥りますので、いったんアクションフックを削除し、保存後に再設定しています。
カスタムフィールドへの保存は、新規保存をし、すでに同名キーが存在すれば追加保存しています。
なお、カスタムフィールドのキー名の先頭にアンダーバー _
を使いますとそのカスタムフィールドはスティルスになります。
02single.php
目次の表示は、記事内の目次を表示したい位置に {toc}
を挿入しておき、single.php でカスタムフィールドの目次を呼び出して置き換えます。
ショートコードを使う方法もありますが、私は AdSense を挿入するためにフィルターフックを使っていますので下のようにこの方法でやっています。
add_filter('the_content', 'add_toc');
function add_toc($content){
global $post;
if( strpos( $content, '{toc}') !== false ){
$meta_value = get_post_meta( $post->ID, 'toc', true );
$content = str_replace( '{toc}', $meta_value, $content );
}
}
あとは目次 id="toc" class="table-of-contents"
をすきに装飾するだけです。
- この方法で目次を表示しているサイト
03ライセンス等
ご使用の場合は以下の注意事項をお守りください。
- ライセンスは IMUZA.com にあります。
- 紹介は歓迎ですが、バグ対応ができなくなりますので転載はしないでください。
- 紹介していただく場合は、当記事へのリンクをお願いします。
- 自己責任でお使いください。
- お問い合わせ、バグの報告、仕様変更のご要望等は Contact Us までお願いします。