任意のIDのエリア内の見出しを構造化したリストにする

経緯

コリスでも「[JS]コンテンツの見出しを目次にし、スクロールしてもサイドバーに固定表示されるナビゲーション -Progress Nav」のように紹介されたProgress Navを使いたかったことと、いちいち見出しをマークアップするのがめんどくなりそうだったので作成しました。

コード

HTMLと、JSは以下の通り

HTML

<div> <div id="single-nav"></div> <div id="single-content-container"> <h2>H2</h2> <p>H2,H2,H2,H2,H2,H2</p> <h3>H3</h3> <p>H3,H3,H3,H3,H3</p> <h4>H4</h4> <p>H4,H4,H4,H4,H4</p> <h2>H2</h2> <p>H2,H2,H2,H2,H2,H2</p> </div> </div>

JS

$(function(){ var $singleNav = $('#single-nav'); var singleNavArr = new Array(); makeSingleNav(); function makeSingleNav(){ //必ず先頭は h2であること var i = 0; var this_tagname; var this_title; var create_html_before = '<ol>'; var create_html_after = '</li></ol>'; var tags = $('#single-content-container h2, #single-content-container h3, #single-content-container h4'); if (!$singleNav.length || !tags.length) { $singleNav.remove(); return false; } $(tags).each(function(index){ this_tagname = $(this)[0].tagName; var isLast = (index == tags.length - 1); var fn = "auto-heading-list-"+i; var html = '<div><a href="#' + fn + '">' + $(this).text() + '</a></div>'; if( this_tagname == "H2") { $(this).attr("id",fn); if (index === 0) { create_html_before += '<li>' + html; } else { switch (tags.eq(i-1)[0].tagName){//直前のリンク case "H2": create_html_before += '</li><li>' + html; break; case "H3": create_html_before += '</li></ol></li><li>' + html; break; case "H4": create_html_before += '</li></ol></li></ol></li><li>' + html; break; } } } else if( this_tagname == "H3") { h(isLast,$(this),"H2","H4"); } else if( this_tagname == "H4") { h(isLast,$(this),"H3","");//H4がミニマムであるので、最後は空にする } i++; if(isLast) { $('#list-single-nav').prepend(create_html_before+create_html_after).find('a').on('click',function(e){ e.preventDefault(); var href = $(this).attr('href'); var position = $(href == '#' || href == '' ? 'html' : href).offset().top; $('html, body').stop().animate({scrollTop:position}, 500); return false; }); } }); function h(isLast,elm,upLevel,downLevel) {//h3かh4の場合の生成 var fn = "auto-heading-list-"+i; var html = "<div><a href='#"+fn+"'>"+elm.text()+"</a></div>"; elm.attr("id",fn); if( isLast ) { if (elm[0].tagName === 'H3' ) { switch (tags.eq(i-1)[0].tagName){//直前のリンク case "H2": create_html_before += '<ol><li>' + html + '</li></ol>'; break; case "H3": create_html_before += '</li><li>' + html + '</li></ol>'; break; case "H4": create_html_before += '</ol></li><li>' + html + '</li></ol>'; break; } } else { switch (tags.eq(i-1)[0].tagName){//直前のリンク case "H3": create_html_before += '<ol><li>' + html + '</li></ol></li></ol>'; break; case "H4": create_html_before += '</li><li>' + html + '</li></ol></li></ol>'; break; } } } else { if (tags.eq(i-1)[0].tagName == upLevel) {//直前見出しが上位の場合一段下げる create_html_before += "<ol><li>" + html; } else if (tags.eq(i-1)[0].tagName == downLevel){//直前が下位の場合は一段上げる create_html_before += "</li></ol></li><li>" + html; } else {//同じレベルの場合は、並列関係 create_html_before += "</li><li>" + html; } } } } });

補足その1 制限

  • h2が先頭に来ること
  • h2からh4までしか収集しないようにする
  • h4の次はh2が来てもいいが、h2の次は必ずh3かh4であること。(h2の次の見出しがh4であってはならない)

補足その2 今後

無駄な場合分けがある気がするので、いずれ直したい

参考サイト

追記

wordpressの場合、投稿や固定ページの記事に記述したH1からH6までの見出し用のHTMLタグを基に目次を自動生成してくれるTable of Contents Plusというプラグインがあるようです。

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

内容に問題なければ、下記の「コメントを送信する」ボタンを押してください。


  1. KATOON.NET
  2. TRASH
  3. 任意のIDのエリア内の見出しを構造化したリストにする