IMUZA.com

<<WordPress(ConoHa)<<はてなブログ

ホーム / php / WordPress:目次をプラグインなしで自動作成、自動表示する

WordPress:目次をプラグインなしで自動作成、自動表示する

2022/01/30 php, Wordpress

このブログはまだはてなブログですが、WordPress に移行したサイトがあり、プラグインなしで目次を自動作成、自動表示する機能を作成しました。

  • WordPress サイト「そんなには褒めないよ。映画評」
  • 設計方針
  • functions.php
  • single.php
  • ライセンス等

設計方針

  • 投稿の新規追加、更新時に目次を作成する
  • 目次は h2, h3 要素から作成する(h4〜も可)
  • 目次はカスタムフィールドに保存する
  • 目次の表示は投稿ページ表示時にカスタムフィールドを呼び出す
  • すでに目次が挿入されている記事があることを想定する

functions.php

ご利用になる場合は、iframe など空要素になるタグで問題があります。次の記事をお読みください。

  • WordPressでiframeが表示されない(解決)

アクションフック save_post を使って、記事の投稿、更新時に目次を作成してカスタムフィールドに保存します。

  • アクションフック 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 として追加し、目次からのページ内リンクを貼ります。

  • DOMDocument
  • DOMXPath

h2, h3 要素の id を追加していますので投稿内容を再保存する必要があります。ただ、そのまま wp_update_post($post); としますと無限ループに陥りますので、いったんアクションフックを削除し、保存後に再設定しています。

カスタムフィールドへの保存は、新規保存をし、すでに同名キーが存在すれば追加保存しています。

  • add post meta

なお、カスタムフィールドのキー名の先頭にアンダーバー _ を使いますとそのカスタムフィールドはスティルスになります。

single.php

目次の表示は、記事内の目次を表示したい位置に {toc} を挿入しておき、single.php でカスタムフィールドの目次を呼び出して置き換えます。

ショートコードを使う方法もありますが、私は AdSense を挿入するためにフィルターフックを使っていますので下のようにこの方法でやっています。

  • WordPress:DOMDocumentを使って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" をすきに装飾するだけです。

  • この方法で目次を表示しているサイト

movieimpressions.com

ライセンス等

ご使用の場合は以下の注意事項をお守りください。

  • ライセンスは IMUZA.com にあります。
  • 紹介は歓迎ですが、バグ対応ができなくなりますので転載はしないでください。
  • 紹介していただく場合は、当記事へのリンクをお願いします。
  • 自己責任でお使いください。
  • お問い合わせ、バグの報告、仕様変更のご要望等は Contact Us までお願いします。
抗原検査キット 唾液検査 新型コロナウイルス 変異株対応 日本製 精度99.4% 自宅で最短約15分スピード検査 1回分 PCR検査 子供 COVID-19 セルフ検査 (研究用) 【即納】【指定名義で領収書発行可能】 (1)

抗原検査キット 唾液検査 新型コロナウイルス 変異株対応 日本製 精度99.4% 自宅で最短約15分スピード検査 1回分 PCR検査 子供 COVID-19 セルフ検査 (研究用) 【即納】【指定名義で領収書発行可能】 (1)

  • SUCCUL

Amazon

WordPress:DOMDocumentを使ってAdSenseを挿入する
Conoha Wing 障害発生、問い合わせするも無視される
Twitter
Facebook
ブックマーク
LINEで送る

最初のサイドバー

最新記事

2022/07/29

WordPressでiframeが表示されない(解決)

2022/07/14

WordPressのデータベースを入れ替える

2022/07/7

WordPress:テーマをゼロから作ってみる(3)

2022/06/26

WordPress:Aレコード変更でウェブだけ他サーバへ移転

2022/06/13

WordPress Popular Posts 他のプラグインに変えたほうがいいかも

最新記事を一覧で見る

よく読まれている記事

よく読まれている記事を一覧で見る

カテゴリー

  • はてなブログ212
  • WebTips108
  • javascript95
  • Joomla!88
  • Windows68
  • CSS62
  • Joomla!更新53
  • Linux49
  • はてなテーマ45
  • Wordpress37
  • Plamo33
  • Google32
  • はてなプラグイン25
  • php19
  • Node.js18
  • SASS16
  • Ubuntu16
  • laravel415
  • Chrome10
  • cms-style10
  • iPhone9
  • genesis6
  • Git入門6
  • ConoHa WING5
  • Python4
  • Android4
  • SSD3
  • Docker3
  • スマートフォン3
  • Facebook3
  • Firefox3
  • 静的サイトジェネレーター3
  • Blankslate3
  • Mactype2
  • GitHub2
  • rails入門1
  • はてなブクマ1
  • Twitter1
  • 映画1
  • youtube1

Footer

My Web Sites

  • @半径とことこ60分
  • そんなには褒めないよ。映画評
  • IMUZA.com
  • GitHub

Another Sites

  • WordPress公式
  • WordPress関数リファレンス
  • PHPマニュアル

Contact Us

  • お問い合わせフォーム
  • Twitter
  • Facebook
  • Feedly

Copyright © 2022 · IMUZA.com