【jQuery】ページ内の目次を自動出力|h2・h3要素を目次の項目に

ウェブサイトやブログを作成していると、ページ内に目次を作成したい場合があるかもしれませんが、毎回、自分自身で目次を作成するのは面倒だと思います。
今回は、そのような場合に使用できるjQueryのソースコードとなり、ページ内の目次を自動で作成して出力する例となります。
また、PHPで目次を自動作成する方法については、以下のリンク先を参考にしてみてください。
jQueryでページの目次を自動作成する例
目次が作成される条件
今回の例では、ページ内の任意の範囲内にあるh2・h3要素が3つ以上ある場合に、それらの要素から自動で目次を作成して出力します。
また、目次の先頭の項目がh2要素となる場合のみに使用できます。
作成する目次の特徴
目次として出力されるHTMLはol要素で出力され、目次の項目にはページ内のそれぞれの箇所へのアンカーリンクを追加します。
また、ページ内でh2要素の次にh3要素がある場合には、目次が階層化されol要素をネスト(入れ子)にして出力します。
jQueryのソースコード
以下、目次を自動作成するjQueryのソースコードの例となります。
jQuery(function($){
// 目次の出力に使用する変数
var toc = '<h2>目次</h2><ol>';
// 目次の階層の判断に使用する変数
var hierarchy;
// h2・h3の判断に使用する変数
var element = 0;
// 目次の項目数をカウントする変数
var count = 0;
$('#toc-range h2, #toc-range h3').each(function(){
// 目次の項目数のカウントを増加
count ++;
// h2・h3タグにIDの属性値を指定
this.id = 'chapter-' + count;
// 現在のループで扱う要素を判断する条件分岐
if(this.nodeName == 'H2'){
element = 0;
}else{
element = 1;
}
// 現在の状態を判断する条件分岐
if(hierarchy === element){ // h2またはh3がそれぞれ連続する場合
toc += '</li>';
}else if(hierarchy < element){ // h2の次がh3となる場合
toc += '<ol>';
hierarchy = 1;
}else if(hierarchy > element){ // h3の次がh2となる場合
toc += '</li></ol></li>';
hierarchy = 0;
}else if(count == 1){ // 最初の項目の場合
hierarchy = 0;
}
// 目次の項目を作成。※次のループで<li>の直下に<ol>タグを出力する場合ががあるので、ここでは<li>タグを閉じていません。
toc += '<li><a href="#' + this.id + '">' + $(this).html() + '</a>';
});
// 目次の最後の項目をどの要素から作成したかにより、タグの閉じ方を変更
if(element == 0){
toc += '</li></ol>';
}else if(element == 1){
toc += '</li></ol></li></ol>';
}
// ページ内のh2・h3タグが3つ以上の場合に目次を出力
if(count < 3){
$('#toc').remove();
}else{
$('#toc').html(toc);
}
});
目次を表示する際に必要なHTMLのマークアップ
目次を表示させたい場所に、以下のHTMLを記述します。
<div id='toc'></div>
目次に使用したいh2・h3要素がある範囲を、以下のdivタグで囲みます。
<div id='toc-range'>
<!-- 目次に使用したいh2・h3要素がある範囲 -->
</div>
ソースコードの説明
例では、目次に使用するタグの条件は、<div class="toc-range"></div>内にあるh2・h3要素から、目次を自動で作成して<div id='toc'></div>の箇所に出力して表示します。
以下は、jQueryのソースコードのポイントとなる箇所の説明となります。
ループ内で使用する変数を宣言・定義
まず、ループ内で使用する変数を宣言・定義します。例では以下の箇所となります。
例のソースコード2行目からの箇所
// 目次の出力に使用する変数
var toc = '<h2>目次</h2><ol>';
// 目次の階層の判断に使用する変数
var hierarchy;
// h2・h3の判断に使用する変数
var element = 0;
// 目次の項目数をカウントする変数
var count = 0;
eachループでh2・h3要素を扱う
次に、eachループを使いページ内にあるh2・h3要素を扱い目次を作成していきます。
eachループ内ではまず、ループ回数をカウントする変数countの値を1増加させます。そして、現在のループで扱っている要素にIDを追加します。例では以下の箇所となります。
例のソースコード11行目からの箇所
$('#toc-range h2, #toc-range h3').each(function(){
// 目次の項目数のカウントを増加
count ++;
// h2・h3タグにIDの属性値を指定
this.id = 'chapter-' + count;
// 中略
});
現在のループで扱う要素を判断
次に現在のループで扱っている要素がh2かh3を判断して変数elementの値を変更します。例では以下の箇所となります。
例のソースコード17行目からの箇所
// 現在のループで扱う要素を判断する条件分岐
if(this.nodeName == 'H2'){
element = 0;
}else{
element = 1;
}
現在の状態を判断する条件分岐
今回の例では、eachループでページ内にあるh2・h3要素を扱い目次を作成しますが、h2・h3要素の順番によって目次を入れ子にします。
そのため、現在の目次の階層を判断し、目次で使用するHTMLタグや変数hierarchyの値を変更します。例では以下の箇所となります。
例のソースコード24行目からの箇所
// 現在の状態を判断する条件分岐
if(hierarchy === element){ // h2またはh3がそれぞれ連続する場合
toc += '</li>';
}else if(hierarchy < element){ // h2の次がh3となる場合
toc += '<ol>';
hierarchy = 1;
}else if(hierarchy > element){ // h3の次がh2となる場合
toc += '</li></ol></li>';
hierarchy = 0;
}else if(count == 1){ // 最初の項目の場合
hierarchy = 0;
}
目次の項目を作成
次に現在のループで扱う要素から目次の項目を作成します。また、次のループで扱う要素によっては、HTMLタグを変更し目次の入れ子の状態を変更したいため、ここでは<li>タグを閉じていません。
例では以下の箇所となります。
例のソースコード37行目からの箇所
// 目次の項目を作成。※次のループで<li>の直下に<ol>タグを出力する場合ががあるので、ここでは<li>タグを閉じていません。
toc += '<li><a href="#' + this.id + '">' + $(this).html() + '</a>';
目次の最後の項目をどの要素から作成したかによりタグの閉じ方を変更
eachループから抜けると目次の項目はHTMLで作成された状態になりますが、目次の最後の項目をどの要素から作成したかにより、HTMLタグの状態が変わりますので、最後に閉じるHTMLタグも変更します。
例では以下の箇所となります。
例のソースコード41行目からの箇所
// 目次の最後の項目をどの要素から作成したかにより、タグの閉じ方を変更
if(element == 0){
toc += '</li></ol>';
}else if(element == 1){
toc += '</li></ol></li></ol>';
}
目次を出力する条件
最後に、目次を出力する条件を以下の箇所で判断しています。今回の例では、目次で使用するh2・h3要素がページ内に3つ以上ある場合に目次が出力されます。
例のソースコード48行目からの箇所
// ページ内のh2・h3タグが3つ以上の場合に目次を出力
if(count < 3){
$('#toc').remove();
}else{
$('#toc').html(toc);
}