WordPressの記事中にプラグインなしで目次を追加しました。

テーマを自作してからも少しづつ機能を追加しているんですが、今日は、記事中にプラグインなしで目次を追加しましたので参考にした記事とともにご紹介します。

完成形

今回の完成形は以下の画像の通りです。

実装したかった機能

目次を作成するにあたって実装したかった機能がいくつかありました。

  1. 見出しタグ(h2h3など)から自動的に作成する
  2. ページ内リンクはスムーススクロールさせたい
  3. 過去記事にも自動的に挿入したい(新たに編集する必要がないようにしたい)

以上のイメージでググってみた結果、いくつか参考になりそうな記事が出てきました。

参考にした記事

その中で実際に参考にしたのは以下の記事です。

実装方法

詳細は上の記事を読んでいただくとして、当ブログでの実装方法を記しておきます。

jQuery

最初にjQueryファイルを作成します。ファイル名はtoc.jsとしました。目次を英訳した「Table of contents」の頭文字を取った略字ですね。中身は以下の通りです。

jQuery(function() {
    var idcount = 1;
    var toc = '';
    var currentlevel = 0;
    $(".post_body h2, .post_body h3, .post_body h4, .post_body h5, .post_body h6",this).each(function(){
        this.id = "chapter-" + idcount;
            idcount++;
        var level = 0;
        if(this.nodeName.toLowerCase() == "h2") {
            level = 1;
        } else if(this.nodeName.toLowerCase() == "h3") {
            level = 2;
        } else if(this.nodeName.toLowerCase() == "h4") {
            level = 3;
        } else if(this.nodeName.toLowerCase() == "h5") {
            level = 4;
        } else if(this.nodeName.toLowerCase() == "h6") {
            level = 5;
        }
        while(currentlevel < level) {
            toc += "<ol>";
            currentlevel++;
        }
        while(currentlevel > level) {
            toc += "</ol>";
            currentlevel--;
        }
        toc += '<li><a href="#' + this.id + '">' + $(this).html() + "</a>n";
    });
    while(currentlevel > 0) {
        toc += "</ol></li>";
        currentlevel--;
    }
    if(toc){ toc = '<div class="toc_title">contents</div>' + toc + '</div>'; }
    $(".toc").html(toc);

// ページ内リンクのスムーススクロール・「#」非表示
$('a[href^="#"]').click(function() {
      var speed = 1000;
      var href= $(this).attr("href");
      var target = $(href == "#" || href == "" ? 'html' : href);
      var position = target.offset().top;
      $('body,html').animate({scrollTop:position}, speed, 'swing');
      return false;
   });
});

ポイントは5行目の

$(".post_body h2, .post_body h3, .post_body h4, .post_body h5, .post_body h6",this).each(function(){

なんですが、ここで見出しタグのh2h3を指定する際に、当ブログでは.post_body内にある見出しタグだけを拾いたいので、見出しタグの前に.post_bodyを付しています。この辺はご自身のサイトに合わせて拾うべき見出しタグの指定を行ってください。下の方にはスムーススクロール等の記述があります。このファイルを保存し、サーバーにアップします。当ブログではテーマディレクトリのjsフォルダ内にアップしました。

HTML

続いては、先ほどアップしたtoc.jsをHTMLで呼び出します。任意の場所に以下を追加します。

<script src="<?php echo get_template_directory_uri(); ?>/js/toc.js"></script>

もちろんjQuery本体も呼び出しといてください。ここまで順調にいけば、もう少しです。最後は記事中に以下のようにコードを記載すればその箇所に自動的に目次が表示されます。

<div class="toc"></div>

CSS

ご参考までに当ブログで指定しているCSSを記載しておきます。

.toc {
    margin-bottom: 48px;
    border: 1px solid #cecece;
}

.toc_title {
    font-size: 2.0rem; /* 20px */
    font-weight: 600;
    text-transform: uppercase;
    text-align: center;
    padding: 8px;
    border-bottom: 1px solid #cecece;
    background: #f7f7f7;
}

.toc_title:before {
    font-family: "Font Awesome 5 Free";
    padding-right: 8px;
    content: "f03a";
}

.toc ol {
    line-height: 1.8;
    list-style: none;
    counter-reset: section;
}

.toc > ol {
    padding: 24px;
}

.toc > ol ol {
    padding-left: 16px;
}

.toc > ol ol ol {
    padding-left: 32px;
}

.toc ol>li:before {
    counter-increment: section;
    content: counters(section, "-") ".";
}

入れ子リストの番号表示の指定ってCSSでできるんですね。 勉強になりました。

最初のh2直前に自動的に表示させる

ここまででも十分なんですが、上記のままだと今後もそうですが過去記事も一つ一つ編集を加えないといけませんので大変です。そこでWordPressの万能神であるfunctions.php様に以下のように追加します。

// 記事中の最初のh2前に目次を追加
function toc_in($the_content) {
  if (is_single()) {
    $toc = "<div class="toc"></div>";

    $h2 = '/<h2.*?>/i';//H2見出し
    if ( preg_match( $h2, $the_content, $h2s )) {
      $the_content  = preg_replace($h2, $toc.$h2s[0], $the_content, 1);
    }
  }
  return $the_content;
}
add_filter('the_content','toc_in');

これだけで、記事中の最初に出てくるh2タグの直前に目次を自動的に表示してくれますので、過去記事も編集なしでオッケーです。

最後に

いかがでしたでしょうか。ショートコードを使って自分の好きな位置に挿入する方法でもいいんですが、それだと挿入し忘れたりするのでやっぱり全自動がいいですよね。あと欲を言えば表示、非表示のボタンを付けたいかなと思っています。これもできそうっちゃできそうなので時間があればやってみます。

※H30.7.7追記 ボタンは付けてないけど開閉式にしました。

「CONTENTS」の部分をクリックすることで開いたり閉じたりします。分かりにくいけど果たしてそこまで開閉式が必要かなとも思い始めたので、あえて分かりにくくしました(笑)実装方法は別記事でご紹介します。