記事を見やすくするために段落分けして見出しを取り付けるのは非常に良いことです。しかしせっかく見出しを付けたのにそれを一覧で表示してくれる「目次」のようなものが無かったら残念です。
そこで、記事の内容から自動的に目次を生成して、それをいい感じに表示してくれる機能をプラグインなしで、作っていきたいと思います。
プラグインを使わないメリット
最初にざっと、WordPressプラグインを使わないメリットについてお伝えします。
記事の内容から目次を自動的に生成して先頭に表示してくれるWordPressのプラグインは実は存在していて、ちょっと前までBableTechでも使っていました。
しかしプラグインを使うと記事のロード時間が長くなってしまったり、自分のサイトに合わせたデザインや機能を盛り込みにくいというデメリットがあります。
WordPressにおいては記事を読み込むとき、function.phpなどを含むテーマファイルやプラグインファイルが読み込まれ、そして記事が読み込まれてロード完了となります。 つまりページが表示される前にプラグインの処理等が行われるわけですが、目次というのは記事の内容から自動生成すれば良いだけのものなので、記事が読み込まれた後に目次作成処理を行っても良いわけです。
そうなってくると、わざわざプラグインに組み込んでロード時間を長くするよりも、ロード完了後に自動実行するJavaScriptのプログラムを作る方が、ロード時間を短くすることができるのでSEO対策的にも良いです。それに、JavaScriptを活用することでスクロールに合わせた処理、目次ウィンドウの開閉等、便利な機能を盛り込みやすくなります。
なお、JavaScriptで機能を作る際の注意点としては、
- 処理が多すぎると重くなって結局離脱率が向上してしまう
- ロード完了後にHTML要素をがちゃがちゃ動かしすぎるとGoogleからの評価が低くなる
- そもそもJavaScriptを無効にしている利用者は使えない
などがあげられますね。3番に関しては、まぁ目次がなくなったからと言って記事の内容が見えなくなるわけでもないので許容しましょう。3番を解決しようと思ったらプラグイン等を使って事前にPHPで目次を作成する(もしくは記事作成時に自分で目次を記述する)しかないですね。
jQueryで自動目次挿入システムを実装する
ということで、早速自動目次挿入システムを作っていきたいと思います。JavaScriptで実装するといっても色々方法はあるわけですが、WordPressと相性が良い「jQuery」を活用していきましょう。jQueryはオワコンとか言われがちですが、最新のWordPressでもjQueryは標準でロードされる仕様になっているみたいなので、WordPressを使っている以上jQueryを活用した方がむしろ良いかと思います。
(脱jQueryをしたいならそもそもWordPress自体を使わない方がいいですね…)
※この記事はある程度JavaScriptの知識がある人向けとなっておりますのでご注意ください
記事から見出し情報を取得する
まずは記事から見出しの情報をスクレイピングしていきましょう。 ただ、記事本文以外にもサイドバーとかヘッダー、フッターがあったりしてそこでもh3とかの見出しが使われている可能性があるので、まず最初は記事本文を含んでいるエリアを探します。
Google Chromeの開発者ツールなどを使うと探しやすいですかね。
通常は「article」などのタグの中に含まれていることが多いですが、ブログによってはarticleタグが同じページの中に複数あったりするので特定できるようにCSSのセレクタを作ってください。
そして、目次を作りたいPHPテンプレート(single.php:投稿、page.php:固定ページ…etc)上に直接書くかもしくはJSファイルを作ってテンプレート上で読み込ませつつ、以下のようなコードを書いてみましょう。
jQuery(function(){
const main_content = jQuery("div.entry-content"); //記事本文を覆っている要素を取得
const h2_h3_list = main_content.find("h2,h3"); //h2とh3要素を取得する
h2_h3_list.each(function(){
const tag_name = jQuery(this).prop("tagName"); //H2やH3など、タグの種類を取得する
const text = jQuery(this).text(); //見出しのテキストを取得
console.log(tag_name,text); //確認用
});
});
そして任意の記事を表示させると、ページロード完了時にコンソールに見出しのタグの名前とテキストが出力されたことが確認できます。

iPhone SEの後継「iPhone SE2 / iPhone 9」に関する情報をまとめてみた
BableTechのこの記事だと、こういう風に出力されましたね。しっかりタグが認識できています。続いて、この見出し情報から目次のHTMLコードを作っていきましょう。
jQuery(function(){
const main_content = jQuery("div.entry-content"); //記事本文を覆っている要素を取得
const h2_h3_list = main_content.find("h2,h3"); //h2とh3要素を取得する
var index_DOM = ""; //目次のHTML
h2_h3_list.each(function(index){
const tag_name = jQuery(this).prop("tagName"); //H2やH3など、タグの種類を取得する
const text = jQuery(this).text(); //見出しのテキストを取得
index_DOM += `<li class="${tag_name}"><a>${text}</a></li>`; //この見出しのli要素を作成
});
console.log(index_DOM); //確認用
});
コードを以上のように書き換えてページを更新してみると、今度はコンソールにそれっぽいHTMLコードが出力されたと思います。 そしてli要素のクラスにタグの名前が追加されているため、h2,h3などタグの種類によってデザインを変更することができます。
目次を表示する
いよいよ準備が整ったところで、目次を表示させてみましょう。投稿テンプレート内の目次を埋め込みたい場所に以下のようなdiv要素を入れます。
<div id="post_index"><h2>目次</h2><ul></ul></div>
ちなみに以下の記事を参考にして自作ショートコードを作れば、投稿の編集画面から好きなタイミングで目次を呼び出すことができるようになります。
【1分で実装】WordPressで自作のショートコードを作る方法【PHP】
1分でできるので是非やってみてください。
jQuery(function(){
const main_content = jQuery("div.entry-content"); //記事本文を覆っている要素を取得
const h2_h3_list = main_content.find("h2,h3"); //h2とh3要素を取得する
var index_DOM = ""; //目次のHTML
h2_h3_list.each(function(){
const tag_name = jQuery(this).prop("tagName"); //H2やH3など、タグの種類を取得する
const text = jQuery(this).text(); //見出しのテキストを取得
index_DOM += `<li class="${tag_name}"><a>${text}</a></li>`; //この見出しのli要素を作成
});
jQuery("div#post_index ul").html(index_DOM); //HTML出力
});
そして上のようにHTML出力したら、目次がしっかり表示されているのが確認できます。CSSを変えるのは後にしましょう。

んーまだ見にくいけど、ちゃんと表示されています!
jQuery(function(){
const main_content = jQuery("div.entry-content"); //記事本文を覆っている要素を取得
const h2_h3_list = main_content.find("h2,h3"); //h2とh3要素を取得する
var index_DOM = ""; //目次のHTML
h2_h3_list.each(function(){
const tag_name = jQuery(this).prop("tagName"); //H2やH3など、タグの種類を取得する
const text = jQuery(this).text(); //見出しのテキストを取得
index_DOM += `<li class="${tag_name}"><a href="#">${text}</a></li>`; //この見出しのli要素を作成
});
jQuery("div#post_index ul").html(index_DOM); //HTML出力
jQuery("div#post_index ul li a").on('click',function(){
const index = jQuery("div#post_index ul li a").index(this); //クリックされたのが目次の何番目の要素か
const that_y = h2_h3_list.eq(index).offset().top; //クリックされた見出しの記事内でのy座標
jQuery("body,html").animate({scrollTop:that_y - 50},1000); //見出しまで滑らかに移動
return false;
})
});
そしてこのコードを書くことで、目次の見出しをクリックしたら自動でその場所まで運んでくれるようになります。
CSSを整える
そしてCSSをいい感じに整えて、以下のようになりました。

デザインのセンスが壊滅的かもしれませんが、まださっきよりは見やすいでしょう。この記事では最初にh2見出しよりも先にh3見出しが来てしまっているので変な階層構造になっていますが、記事執筆時にしっかりとした階層構造を意識すればこうはならないので大丈夫です。 ちなみにCSSについては以下を参考程度にどうぞ
div#post_index {
height: 270px;
background-color: #fffcf5;
border-radius: 10px;
margin: 30px;
box-shadow: 0 0 10px #c0b499a8;
}
div#post_index h2 {
text-align: center;
padding: 5px;
line-height: 30px;
font-size: 20px;
margin-bottom: 10px;
background-color: #ffeecf;
}
div#post_index ul {
height: 220px;
overflow-y: scroll;
}
div#post_index ul li {
list-style: none;
margin: 5px 10px 5px 10px;
}
div#post_index ul li a {
font-size: 15px;
color: black;
text-decoration: none;
font-weight: bold;
}
div#post_index ul li.H3 a {
font-size: 12px;
position: relative;
margin-left: 25px;
opacity: .8;
}
div#post_index ul li.H3 a:before {
content: ">";
margin-right: 5px;
}
ちなみに、ulの高さを固定することで目次が挿入されたときにHTML要素がごそっと動かないようにしています(ごそっと動いてしまうとGoogleからの評価が下がってしまう…)
そして見出しの数が多すぎたとき用に、overflow-y:scroll;を入れてスクロールできるようにしています。
いろんなサイトを見ていると、目次を非表示にできる謎の機能とか親見出しをクリックしたら小見出しを表示できる謎の機能を盛り込んでいるサイトが多いように感じますが、そのような機能はあまり必要ない気がしますね。目次に表示する見出しの階層構造は2段くらいが見やすいですし、それくらい浅かったら展開機能は要らないでしょう。 目次非表示機能は… 本当に何のためにあるんですかね。
今回はひとまずこれで終わりです。今度、スクロールと連動して目次がついてきてくれる機能も作ろうかと思います。お楽しみに~