JavaScriptでブログ記事の目次を自動生成するプログラム
JavaScriptでブログ記事の目次を自動生成するプログラム
説明
ブログ記事の見出しを抜き出して目次としてまとめます。
見出しを抜き出す範囲の変更、見出しとして扱う要素の変更・追加が可能です。
※このプログラムは見出しとなる要素のidの値を書き換えます。他のプログラムと競合する可能性がありますので動作が不安定になった場合は一度使用を中止してください。
コード
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* タイトル : JavaScriptで目次を自動生成する
* バージョン : 1.0.0
* 製作者 : EthanFjp
* サイトURL : https://ethanfjp.com/
* 最終更新 : 2021/09/10
* 説明 : htmlの目次を生成したい場所に <ul id="toc"></ul> または <ol id="toc"></ol> を設置して、このjsファイルを読み込む。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*==============================================================================
* メインの処理
================================================================================*/
function mktoc()
{
'use strict';
//-------(※設定項目)-----------------------------------------
//--
//設定項目1 : 目次の項目として抽出する要素 (※大きい見出しから順番に)(追加も可能)
//default : ['h2', 'h3'];
const HEADING_LIST = ['h2', 'h3'];
//--
//設定項目2 : 目次の項目に使用するid名兼class名 (すでに使用中のidやclassと被る場合などに変更)
//default : 'toc_item';
const ADD_ID = 'toc_item';
//--
//設定項目3 : 目次を設置するhtml側と一致するul(ol)要素のid (※html側と合わせて変更すること)
//default : 'toc';
//default(html側) : <ul id="toc"></ul> or <ol id="toc"></ol>
const TOC_ID = 'toc';
//--
//設定項目4 : ページ内で目次要素を抽出する範囲に制限をかけたい場合の指定id
//デフォルト : ''; -> body全体から抽出
const TOC_AREA_ID = '';
//-------(※/ここまで設定項目)--------------------------------
//--
//目次を設置するul要素のelement -> 該当するul要素がhtml側に存在しない場合は処理しない
let tocElement = document.getElementById(TOC_ID);
if(tocElement !== null && tocElement !== void 0){
//--
//作成するタグ名を取得 : html側に追加されたulまたはolを取得
let tagName = tocElement.tagName;
//--
//目次要素を抽出する範囲のelement -> 該当する要素がhtml側に存在しない場合は処理しない
let tocArea = document.getElementsByTagName('body')[0];
if(TOC_AREA_ID !== ''){
tocArea = document.getElementById(TOC_AREA_ID);
}
if(tocArea !== null && tocArea !== void 0){
//--
//抽出する要素のタグ名を1つにまとめる : querySelectorAllにまとめて渡すため
//queryName = 'h2,h3';
let headingListLength = HEADING_LIST.length;
let queryName = '';
for(let i = 0; i < headingListLength; ++i){
queryName += HEADING_LIST[i] + ',';
}
queryName = queryName.slice(0, -1);
//--
//要素を抽出してリストを作成 -> 抽出された要素が1つ以上存在しない場合は処理しない
let matchElement = tocArea.querySelectorAll(queryName);
let matchElementLength = matchElement.length;
if(matchElementLength > 0){
//--
//データの配列に使用する定数
const ID_PRIORITY = 0; //優先度
const ID_ELEMENT = 1; //登録する要素
const ID_CHILDSPACE = 2; //子要素を入れる枠
//--
//データリストを作成
let dataList = [];
for(let i = 0; i < matchElementLength; ++i){
for(let j in HEADING_LIST){
if(matchElement[i].localName === HEADING_LIST[j]){
//--
//html側にリンクに使うidを追加
matchElement[i].id = HEADING_LIST[j] + '_'+ ADD_ID +i;
//--
//データに登録する要素を作成
//<li> <a></a> <ul></ul> </li>
let li = document.createElement('li');
let a = document.createElement('a');
let ul = document.createElement(tagName);
a.href = '#'+ HEADING_LIST[j] + '_'+ ADD_ID + i;
a.title = matchElement[i].innerHTML;
a.innerHTML = matchElement[i].innerHTML;
ul.className = HEADING_LIST[j]+ '_' + ADD_ID;
li.appendChild(a);
li.appendChild(ul);
//--
//データリストにデータを登録
//data = [ID_PRIORITY, ID_ELEMENT, ID_CHILDSPACE];
dataList[i] = [Number(j), li, ul];
}
}
}
//--
//データリストの最初のID_PRIORITYが0ではない場合は0の要素を追加
//処理の都合で最初のデータはID_PRIORITY=0である必要がある
if(dataList[0][ID_PRIORITY] !== 0){
let li = document.createElement('li');
let ul = document.createElement(tagName);
ul.className = HEADING_LIST[0] + '_' + ADD_ID;
li.appendChild(ul);
dataList.unshift([0, li, ul]);
}
//--
//データを順番に取り出して子要素から遡って親要素を探す
//子要素は親要素に追加してしまう
let dataListLength = dataList.length;
for(let i = 1; i < dataListLength; ++i){
for(let j = i - 1; 0 <= j; --j){
if(dataList[i][ID_PRIORITY] > dataList[j][ID_PRIORITY]){
//--
//親要素と子要素に2段階以上の差がある場合は中間の親要素を追加
let dif = dataList[i][ID_PRIORITY] - dataList[j][ID_PRIORITY];
if(dif > 1){
for(let k = dif - 1; 0 < k; --k){
let li = document.createElement('li');
let ul = document.createElement(tagName);
ul.className = HEADING_LIST[k] + '_' + ADD_ID;
ul.appendChild(dataList[i][ID_ELEMENT]);
li.appendChild(ul);
dataList[i][ID_ELEMENT] = li;
}
}
dataList[j][ID_CHILDSPACE].appendChild(dataList[i][ID_ELEMENT]);
break;
}
}
}
//--
//子要素はID_PRIORITY=0の親要素に全て含まれているので該当する親要素だけをhtmlに追加
dataList.forEach(function(data){
if(data[ID_PRIORITY] === 0){
tocElement.appendChild(data[ID_ELEMENT]);
}
});
//--
//htmlから空のul(ol)要素を削除する (※空要素が邪魔にならないなら不要)
let tagList = tocElement.getElementsByTagName(tagName);
let checkCount = tagList.length;
for(let i = 0; i < checkCount; ++i){
let tagListLength = tocElement.getElementsByTagName(tagName).length;
for(let j = 0; j < tagListLength; ++j){
if(tagList[j].innerHTML === ''){
tagList[j].remove();
break;
}
}
}
}
}
}
}
/*==============================================================================
* ページの読み込みで起動
================================================================================*/
window.addEventListener('load', function(){mktoc();}, false);
現在のバージョンと更新履歴
現在のバージョンはver1.0.0です。
使用方法
- htmlの目次を表示する位置に指定のタグを設置する
- JavaScriptのプログラムを読み込む
htmlの目次を表示する位置に指定のタグを設置する
指定のタグは以下のものです。
<ul id="toc"></ul>
<ol id="toc"></ol>
ulもolも箇条書きのタグですが、olは番号が振られます。※cssなどで変更していない場合
idの値はデフォルトで"toc"ですが、JavaScript側の設定で別の値に変更した場合は同じものに変更してください。
JavaScriptのプログラムを読み込む
JavaScriptの読み込み方は別のページ(JavaScriptを読み込む方法)で紹介しています。
ファイルでダウンロードする場合はこのページの一番下にあるダウンロードリンクからダウンロードできます。
設定項目
JavaScriptのコードを書き換えることで設定を変更可能です。コード内の(設定項目)となっている部分を変更してください。
項目 | 説明 |
---|---|
HEADING_LIST | 見出しとして使用するhtmlのタグです。追加・削除も可能です。例えば、h4タグも見出しとして追加する場合は['h2', 'h3', 'h4']というように書き換えてください。 |
ADD_ID | 目次の項目に使用するid名及びclass名です。他のプログラムとid名やclass名が被ってしまったことで動作が不安定な場合はこの値を変更することで解決することがあります。 |
TOC_ID | 目次を表示する位置のidの値です。htmlに追加したul要素又はol要素のidの値と一致させてください。他のプログラムと名前が被ってしまったことで動作が不安定な場合はこの値をhtml側と同時に変更することで解決することがあります。 |
TOC_AREA_ID | 目次の項目を検索する範囲の指定です。空白にしておけばページ内のbody要素全てから検索します。詳しくは下で解説します。 |
追加解説:TOC_AREA_IDの設定詳細
TOC_AREA_IDの値はデフォルト設定で空白です。空白であればページ内のbody要素全体から見出しを検索します。
//--
//設定項目4 : ページ内で目次要素を抽出する範囲に制限をかけたい場合の指定id
//デフォルト : ''; -> body全体から抽出
const TOC_AREA_ID = '';
<body>
<!--目次検索範囲-->
<h2>見出し</h2>
<h3>見出し</h3>
<p>本文</p>
<h3>見出し</h3>
<p>本文</p>
<!--/目次検索範囲-->
</body>
※上記の場合は、h2要素が1つ、h3要素が2つ、目次に登録されます。
TOC_AREA_IDの値を適当に設定して、html側に同じidを持つ何らかのタグを追加することで、そのタグの中の見出しに限定して目次を生成します。
//--
//設定項目4 : ページ内で目次要素を抽出する範囲に制限をかけたい場合の指定id
//デフォルト : ''; -> body全体から抽出
const TOC_AREA_ID = 'toc_area';
<body>
<h2>見出し</h2>
<h3>見出し</h3>
<p>本文</p>
<div id="toc_area">
<!--目次検索範囲-->
<h3>見出し</h3>
<p>本文</p>
<!--/目次検索範囲-->
</div>
</body>
※この例ではTOC_AREA_IDの値に"toc_area"という値を設定し、div要素(id="toc_area")で目次にしたい範囲を指定しています。この例の場合はdiv要素の中のh3要素1つだけが目次として登録されます。
ダウンロード
ダウンロードできない場合やお気づきの点、ご意見ご要望がございましたら、コメントやお問い合わせでご連絡ください。
コードの改善案などを提供していただけるのも嬉しいです。
投稿されたコメント一覧