Table of Contents
このサイトにもブログ機能を実装し、思うことあってブログ記事の書き方にMarkdown記法を使用することにしたので、有名なブログサービス等ではよくある、記事内に自動的に生成する「Table of Contents(目次)」機能を作ってみました。
目次機能を実装
当ブログ記事にあるような目次の開閉部分は省略します。あくまでもヘッディング要素から目次を作るのみにします。
HTML
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Table of Contents Sample</title>
</head>
<body>
<div id="blog-content">
<div id="toc"></div>
<h2>H2のタイトル</h2>
<p>H2の本文</p>
<h3>H3のタイトル</h3>
<p>H3の本文</p>
<h2>H2のタイトル</h2>
<p>H2の本文</p>
<h3>H3のタイトル</h3>
<p>H3の本文</p>
<h3>H3のタイトル</h3>
<p>H3の本文</p>
<h4>H4のタイトル</h3>
<p>H4の本文</p>
</div>
<script src="toc.js"></script>
</body>
</html>
JavaScript
今回もまた、ピュアなJavaScriptで実装してみます。
このブログの<H1>タグはページのタイトルで使っているので、目次要素として取得するのは<H2>から<H6>とします。
toc.js
var headers = Array.prototype.slice.call( document.querySelectorAll('h2,h3,h4,h5,h6'));
var count = 1;
var current = 1;
var tag, level, close;
headers.forEach( function( item, idx )
{
tag = item.nodeName.toLowerCase();
level = Number( tag.substring(1));
close = false;
item.setAttribute( 'id', 'anchor-'+ count );
if( current == level ){
close = true;
}
while( current < level ){
tag += '<ol>';
current++;
}
while( current > level ){
tag += '</ol>';
current--;
}
tag += '<li><a href="#anchor-'+ count +'">'+ item.textContent +'</a>';
if(close){
tag += '</li>';
}
count++;
});
while( current > 1 ){
tag += '</ol>';
current--;
}
document.getElementById('toc').innerHTML = tag;
変数tagに追加していく要素が文字列なのが面白くないので、改良版を作らないと。
スクロール機能を実装
折角なので、目次をクリックしたら目的の位置まで滑らかにスクロールする、も。
JavaScript
toc.js
・・・省略
Array.prototype.slice.call( document.querySelectorAll('#toc a'), 1 ).forEach( function( anchor, idx )
{
anchor.addEventListener( 'click', function(e)
{
e.preventDefault();
window.scrollTo({
top: document.getElementById( anchor.getAttribute('href').substring(1)).getBoundingClientRect().top + window.pageYOffset,
behavior: 'smooth'
});
});
});