3rd シリーズによって実現される目次 |
イントロダクション
この記事は「そうだ!Bloggerの記事に目次を付けよう!」から始まる一連の「Bloggerの記事に目次をJavaScriptで自動的に付与する」シリーズの構成記事です。(3rd シリーズ)
記事一覧は「Bloggerの記事に目次をJavaScriptで自動的に付与する - サポートページ」よりどうぞ。
3nd シリーズでは、2nd シリーズで作成した目次機能に対して、実際に使ってみて改善した方がよい点を改善していきます。
3rd シリーズの前回の記事は「見出しが複数行になった場合の見栄え改良 - 3rd(2) - Bloggerの記事に目次をJavaScriptで自動的に付与する」です。
今回の目的
3rd シリーズで修正・機能追加を行った、目次自動生成プログラムとスタイルシートの配布を行います。スタイルシート
#auto-generated-index_title { border-style:solid; border-width:1px; border-color:#999999; background-color:#eeeeee; font-weight:bold; margin:10px 20px 0px 20px; padding: 0px 0px 0px 8px; } #auto-generated-index_content { border-style:solid; border-width:1px; border-color:#999999; background-color:#ffffff; font-weight:normal; margin:0px 20px 20px 20px; } #auto-generated-index_content_h1 { text-indent:-1em; padding-left:1em; margin-left:1em; } #auto-generated-index_content_h2 { text-indent:-1em; padding-left:1em; margin-left:2em; } #auto-generated-index_content_h3 { text-indent:-1em; padding-left:1em; margin-left:3em; } #auto-generated-index_content_h4 { text-indent:-1em; padding-left:1em; margin-left:4em; } #auto-generated-index_content_h5 { text-indent:-1em; padding-left:1em; margin-left:5em; } #auto-generated-index_content_h6 { text-indent:-1em; padding-left:1em; margin-left:6em; } #auto-generated-index_help { float:right; font-weight:bold; border-width:1px; border-style:solid; width:1em; text-align:center; }
ソースコード
(function () { /* 生成処理制御パラメータ */ var min_auto_generated_index_items = 2; /* 見出しの数がこの数以上になった場合に見出しを挿入する */ /* 目次挿入先要素の id の正規表現 */ var regExp_id_obj_insert_index = new RegExp("^post-body-"); /* 見出し内のタグを目次として使用するかどうか */ var use_headline_htmlTag = false; /* 目次のタイトル */ var index_title = "目次"; /* 目次のヘルプを表示するかどうか */ var view_help = true; /* 見出しに細工がされていたとしても、セキュリティ上の脅威を防止する このフラグを true にすると、use_headline_htmlTag の値にかかわらず、見出し内のタグは無効になります */ var index_secure_run = true; if (index_secure_run) { use_headline_htmlTag = false; } /* ページ読み込み時に目次の生成・挿入処理を実行 */ generateIndex(); /* --- lib --- */ /* HTML を エスケープする html : エスケープしたい html エスケープ後の文字列を返す 注意: {本文に出力する html ソースをエスケープする場合}にのみ使用します タグの属性値 や URL などには使用してはなりません。 */ function escapeHTML(html) { /* 要素のテキストデータとして文字列を設定して、 その要素の HTML を取得すると、HTML 文書として表示する場合に、 エスケープしなければならない文字がエスケープされることを利用 */ var obj = document.createElement('div'); obj.appendChild(document.createTextNode(html)); return obj.innerHTML; } /* 要素に設定されている html から、htmlタグを削除した html を取得 obj : element オブジェクト htmlタグを削除した html を返す 注意: element.textContent が使用可能なブラウザでは、script タグや style タグの中身を取得しますが、 element.textContent が使用できず、element.innerText が使用可能なブラウザでは、script タグや style タグの中身は取得できません */ function getHTMLWithoutHTMLTagFromElement(obj) { /* html のタグ要素を削除したテキストとして要素の値を取得した後、 エスケープ処理する */ var headline_text = obj.textContent || obj.innerText; return escapeHTML(headline_text); } /* --- Main --- */ /* 目次の生成・挿入処理 */ function generateIndex() { /* 投稿本文が格納されている div 要素を発見し、目次を挿入する */ var divs = document.getElementsByTagName("div"); for (var i = 0; i < divs.length; i++) { if (regExp_id_obj_insert_index.test(divs[i].id)) { /* 投稿本文が格納されている div 要素発見時の処理 */ generateIndexForObj(divs[i]); break; } } } /* 目次の生成・挿入処理 obj : 目次の挿入先 */ function generateIndexForObj(obj) { /* 目次として使用する見出しの一覧のHTML */ var html_item = ""; /* 見出しの数 */ var item_count = 0; /* 再帰的に見出しを検索し、目次の HTML を作成する */ generateIndexHTMLRecursive(obj); function generateIndexHTMLRecursive(obj) { /* 見出しの列挙 */ for (var i = 0; i < obj.childNodes.length; i++) { /* 見出しタグの場合、見出しのレベルに応じて書式を設定して記録する */ var originalTagName = obj.childNodes[i].tagName; if (originalTagName !== void 0) { /* void 0 = undefined なので、originalTagName が undefined でなければ処理する */ var tagName = originalTagName.toLowerCase(); if (tagName.lastIndexOf("h", 0) == 0) { var level = Number(tagName.substr(1, tagName.length - 1)); /* タグ hx の x の取り出し */ if (!isNaN(level)) { /* アンカー用の id */ var anchor_id = "auto-generated-index_target" + item_count; if (index_secure_run) { /* 既存の id が細工されている場合の脆弱性を防止するため、既存の id は用いず、新しくジャンプ用の要素を追加する */ var secure_anchor = document.createElement("a"); secure_anchor.setAttribute("id", anchor_id); obj.childNodes[i].insertBefore(secure_anchor, obj.childNodes[i].firstChild); } else { /* アンカー設定 すでに id が設定されている場合は、その id をアンカーに用いる */ if (obj.childNodes[i].id != "") { anchor_id = obj.childNodes[i].id; } else { obj.childNodes[i].id = anchor_id; } } /* 見出しの内容 */ var headline = ""; if (use_headline_htmlTag) { headline = obj.childNodes[i].innerHTML; } else { /* 見出し内の HTMLタグを使用しない場合には、HTML タグを削除する */ headline = getHTMLWithoutHTMLTagFromElement(obj.childNodes[i]); } html_item += "<div id=\"auto-generated-index_content_h" + level + "\"><a href=\"#" + anchor_id + "\">" + headline + "</a></div>\r\n"; item_count++; } } } /* 子ノードに対して再帰的に再帰的に見出しを検索 */ generateIndexHTMLRecursive(obj.childNodes[i]); } } /* 目次のHTMLを本文へ挿入 */ if (item_count >= min_auto_generated_index_items) { var help_html = "<span id=\"auto-generated-index_help\"><a href=\"http://upa-pc.blogspot.jp/p/addindex.html\" title=\"この目次について\" rel=\"nofollow\">?</a></span>"; /* 目次の HTML を事前に要素に変換 */ var index_obj = document.createElement("div"); index_obj.setAttribute("id", "auto-generated-index"); index_obj.innerHTML = "<div id=\"auto-generated-index_title\">" + index_title + (view_help ? help_html : "") + " </div>" + "<div id=\"auto-generated-index_content\">" + html_item + "</div>" + (index_secure_run ? "<!-- セキュアな目次 -->" : ""); /* 本文に目次を挿入 */ obj.insertBefore(index_obj, obj.firstChild); } } })();
ソースコード(最適化後)
特にパラメータを変更する必要が無い場合、最適化後のソースコードを使えば、そのまま自分でソースコードを最適化する必要がなく、そのままソースコードを貼り付けることができます。(function(){function n(a){function f(d){for(var b=0;b<d.childNodes.length;b++){var e=d.childNodes[b].tagName;if(void 0!==e&&(e=e.toLowerCase(),0==e.lastIndexOf("h",0)&&(e=Number(e.substr(1,e.length-1)),!isNaN(e)))){var a="auto-generated-index_target"+h;if(l){var c=document.createElement("a");c.setAttribute("id",a);d.childNodes[b].insertBefore(c,d.childNodes[b].firstChild)}else""!=d.childNodes[b].id?a=d.childNodes[b].id:d.childNodes[b].id=a;c="";if(m)c=d.childNodes[b].innerHTML;else{var c=d.childNodes[b], c=c.textContent||c.innerText,g=document.createElement("div");g.appendChild(document.createTextNode(c));c=g.innerHTML}k+='<div id="auto-generated-index_content_h'+e+'"><a href="#'+a+'">'+c+"</a></div>\r\n";h++}f(d.childNodes[b])}}var k="",h=0;f(a);if(h>=p){var g=document.createElement("div");g.setAttribute("id","auto-generated-index");g.innerHTML='<div id="auto-generated-index_title">'+q+(r?'<span id="auto-generated-index_help"><a href="http://upa-pc.blogspot.jp/p/addindex.html" title="この目次について" rel="nofollow">?</a></span>': "")+' </div><div id="auto-generated-index_content">'+k+"</div>"+(l?"\x3c!-- セキュアな目次 --\x3e":"");a.insertBefore(g,a.firstChild)}}var p=2,s=/^post-body-/,m=!1,q="目次",r=!0,l=!0,m=!1;(function(){for(var a=document.getElementsByTagName("div"),f=0;f<a.length;f++)if(s.test(a[f].id)){n(a[f]);break}})()})();
補足:Blogger に設置する場合
投稿ページにのみ目次を表示するためと、HTMLテンプレートの修正にて正常にJavaScriptコードを埋め込むために、<!-- 目次の自動生成 START -->
<b:if cond='data:blog.pageType == "item"'>
<!-- 投稿ページにのみ目次を表示する -->
<script type='text/javascript'>
//<![CDATA[
<!--
と
//-->
//]]>
</script>
</b:if>
<!-- 目次の自動生成 END -->
の間にソースコードを記載しています。
「Bloggerの記事に目次をJavaScriptで自動的に付与する」シリーズ 記事一覧へ
コメントを投稿 (ここをクリックしてコメント投稿フォームを表示)
コメント投稿機能について