JavaScript: 要素内のHタグをリストし目次を自動生成 ページ内リンクを作成する
Bloggerで見出しを自動インデックス
今ならもっとスマートなコードを書けると思います。これは2007年頃に書いたコード。2019年に手直ししてますがコードはグダグダです。が動きます。
今ならもっとスマートなコードを書けると思います。これは2007年頃に書いたコード。2019年に手直ししてますがコードはグダグダです。が動きます。
投稿へ設置
<script>midashi_generator();</script>
レイアウトでガジェットのHTMLをヘッダエリアに追加
//<script type="text/javascript"> function midashi_generator(undefined) { // ▼------ 設定項目 ここから ------▼ // --- 以下必須指定 --- // 見出しリスト(目次)生成する文字列の親要素を指定(Bloggerで使用する場合は変更不要) var midasi_generation_element = "div[id^='post-body-']"; /* Bloggerテンプレートの場合はポスト本文のコンテナ要素 div#post-body-{postId} セレクターの指定方法は下記URLを参照 Document.querySelectorAll() https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll 属性セレクタ https://developer.mozilla.org/ja/docs/Web/CSS/Attribute_selectors */ // --- 以下オプション指定 --- // 目次を出すページタイプを指定 var pageType = new RegExp("item|static_page|index|archive"); // アイテムページとスタティックページの時に動く // 目次のタイトル var title = "目次"; // 見出しタグの目次化の指定 /* 半角数値で指定 指定がない時は全部の見出しタグをリスト "2-4" とした時は タイトル (H2) と 大見出し (H3) と 小見出し (H4) をリスト "1|3" とした時は ページタイトル (H1) と 大見出し (H3) をリスト */ var choiceHtags = ""; // 目次のリストスタイル指定 /* 半角数値で指定 */ var style1 = ""; // 第一階層(親) var style2 = ""; // 第二階層(子) var style3 = ""; // 第三階層(孫) var style4 = ""; // 第四階層(ひ孫) // 半角英数で指定 // 指定がない時はデフォルトのリストスタイルが適用されます。 // 下記【】内を指定するとリストスタイル(ulタグ又はolタグ)が適用されます // マークなし【none】ul // ・ 記号【disc】ul // 1 2 3…【decimal】ol // A B C…【alpha1】ol // a b c…【alpha2】ol // Ⅰ Ⅱ Ⅲ... 【roman1】ol // ⅰ ⅱ ⅲ... 【roman2】ol // 上記【】内文字に続き半角空白の後に任意の文字列class名を指定することができます。 // ▲------ 設定項目 ここまで ------▲ // STEP0 必須設定項目の確認 if (!midasi_generation_element) { info("設定変数の midasi_generation_element が指定されていません。"); return; } // 目次を出すページタイプを検証(ボディクラスをチェック) //console.log("body.className = " + document.body.className + " /ボディCLASS属性"); if (!pageType.test(document.body.className)) { //console.log("midashi-generator.js: 指定したページタイプ以外なのでスクリプトを中止しました。"); return; } // 当スクリプトを変数にセット var scpt = document.getElementsByTagName("script"); scpt = scpt[scpt.length - 1]; // post-body-postIdを取得 var postBody = getClosestElement(scpt, midasi_generation_element); //console.log(postBody); if (!postBody) { info("見出し生成の範囲を特定できませんでした。見出し生成の範囲の指定を確認してください。"); return; } var postid = postBody.id.split('-')[2]; /* postIdを取得 */ //console.log('postid '+postid); var uniqueness = postid; /* 識別子を調整 */ //console.log("uniquenessの完成 " + uniqueness); function getClosestElement(elem, selector) { /* 条件に合う最も近い祖先要素を取得関数 */ var matchElems = document.querySelectorAll(selector); if(!matchElems.length){return null;} //console.log("bingo"); var ancestorNode = elem.parentNode; while (ancestorNode) { for (var i = 0; i < matchElems.length; i++) { if (matchElems[i] === ancestorNode) return ancestorNode; } ancestorNode = ancestorNode.parentNode; } return null; }; // ページの構築が完了したらloadを呼び出す (function() { if (document.addEventListener) { // opera,safari,mozilla向け document.addEventListener("DOMContentLoaded", load, false); //console.log("addEventListenerでloadをコールした"); } else if (/msie/.test(userAgent)) { // IE向け try { document.documentElement.doScroll("left"); } catch (error) { setTimeout(arguments.callee, 0); return; } load(); } else { // その他 window.onload = load; } })(); // 中断メッセージ function info(message) { var el = document.createElement('span'); el.setAttribute("style", "background-color: yellow;"); el.innerHTML = "midashi-generator.js: Check the error details on the console."; scpt.parentNode.insertBefore(el, scpt.nextSibling); console.error('midashi-generator.js: '+message); } // ロードファンクション function load() { //console.log("loadを開始した"); // STEP1 //対象記事の読み込みを確認する if (!postBody) { var tID1 = null; var tID1i = 0; var tID1 = setInterval(function() { if (!postBody || typeof postBody == "undefined") { if (postBody && postBody.lastChild) { //console.log('被メニュー要素の中に何か表示されました'); core(); // ナビゲーションメニューの作成 clearInterval(tID1); } else { tID1i++; if (tID1i > 10) { ul1.innerHTML = "<li>設定された要素のid属性名【" + postBody.id + "】が存在しません。</li><li>要素のid属性名を再確認してください。</li>"; clearInterval(tID1); } } } }, 300); } else { core(); // ナビゲーションメニューの作成 } // STEP2 function core() { // STEP3 // アーカイブページで次のページが設定されている場合は中止する if (/index|archive/.test(document.body.className)) { //console.log("midashi-generator.js: アーカイブページで jump-link があるのでスクリプトを中止しました。"); var postOuter = getClosestElement(postBody, '.post-outer'); if(postOuter.querySelector(".jump-link")){ //console.log(postOuter.querySelector(".jump-link")); return; } } // ナビゲーションメニューを展開する要素を作る/一階層 // メニューを展開する第一階層のul要素をクリエイト // class属性を設定 if (!style1 || typeof style1 == "undefined") { var navlist = document.createElement('ul'); //navlist.setAttribute("class", "default"); } else if (/none|disc/.test(style1)) { var navlist = document.createElement('ul'); navlist.setAttribute("class", style1); } else if (/decimal|alpha[1-2]|roman[1-2]/.test(style1)) { var navlist = document.createElement('ol'); navlist.setAttribute("class", style1); } else { var navlist = document.createElement('ul'); navlist.setAttribute("class", style1); } // メニューを展開する第一階層のul要素を変数にセット var ul1 = navlist; // 結果:<ul class="default" /> // 被メニュー要素のリスト //console.log("postBody.id " + postBody.id); // 収集見出しの指定 if (!choiceHtags || typeof choiceHtags == "undefined") { choiceHtags = "1-4"; } // セレクターで見出しタグを集める var headings = postBody.querySelectorAll("h1,h2,h3,h4"); // 一階層リストと二階層以降 var style, currentHtag, depth, lastHtag, a, li1, ul2, li2, ul3, li3, ul4, li4; lastHtag = 0; depth = 1; for (var i = 0; i < headings.length; i++) { if (RegExp("h[" + choiceHtags + "]").test(headings[i].tagName.toLowerCase())) { //console.log("Core for Hタグ検出"); // カレントの見出しタグ番号を取得 currentHtag = headings[i].tagName.toLowerCase().replace("h", ""); //console.log("currentHtag " + currentHtag); // アンカー処理:見出しタグにidを追加 headings[i].id = "mg_" + postid + "_" + i; // アンカー処理:メニューリンク要素追加 a = document.createElement('a'); a.setAttribute("href", "#mg_" + postid + "_" + i); //a.setAttribute("onclick", "mg_SmoothScroll(this, '#mg_" + postid+i+"');return false;") a.textContent = headings[i].textContent; // 深度(二回以降) //console.log("lastHtag " + lastHtag); if (lastHtag > 0) { depth = depth + (currentHtag - lastHtag); } if(depth == 0){ // 構造階層が壊れているときエラーメッセージを出す info("見出しタグのツリー構造が成立しませんでした。見出し「"+ headings[i].innerText + "」は<" + headings[i].tagName.toLowerCase() + ">ですが、<h" + lastHtag + ">であるべきです。"); headings[i].setAttribute("style", "background-color: deepskyblue!important;"); headings[i].innerHTML = "<span style='color: white!important; background: black;display: block!important;'>midashi-generator.js: !!! You need to set <" + headings[i].tagName.toLowerCase() + "> to <h" + lastHtag + ">.</span><br />" + headings[i].textContent; return; } //console.log("depth " + depth); // カレントの指定スタイルをセット eval("style=style" + depth + ";"); if (depth == 1) { // 第一階層のリストをセット //console.log("第一階層 depth " + depth); li1 = document.createElement('li'); li1.appendChild(a); //console.log("lastHtag " + lastHtag); //if (lastHtag == 0) { ul1.appendChild(li1); //} } else { // 二階層目以降のカレントul/olをセット //console.log("二階層目以降 depth " + depth); if ((currentHtag - lastHtag) == 1 || (currentHtag - lastHtag) <= 0) { if (!style || typeof style == "undefined") { eval("ul" + depth + "=document.createElement('ul');"); } else if (/none|disc/.test(style)) { eval("ul" + depth + "=document.createElement('ul');"); eval("ul" + depth + ".setAttribute('class', '" + style + "');"); } else if (/decimal|alpha[1-2]|roman[1-2]/.test(style)) { eval("ul" + depth + "=document.createElement('ol');"); eval("ul" + depth + ".setAttribute('class', '" + style + "');"); } else { eval("ul" + depth + "=document.createElement('ul');"); eval("ul" + depth + ".setAttribute('class', '" + style + "');"); } }else{ // 構造階層が壊れているときエラーメッセージを出す info("見出しタグのツリー構造が成立しませんでした。見出し「"+ headings[i].innerText + "」は<" + headings[i].tagName.toLowerCase() + ">ですが、<h" + depth + ">であるべきです。"); headings[i].setAttribute("style", "background-color: lightpink!important;"); headings[i].innerHTML = "<span style='color: white!important; background: black;display: block!important;'>midashi-generator.js: !!! You need to set <" + headings[i].tagName.toLowerCase() + "> to <h" + depth + ">.</span><br />" + headings[i].textContent; return; } // カレントliをセット eval("li" + depth + "=document.createElement('li');"); eval("li" + depth + ".appendChild(a);"); eval("ul" + depth + ".appendChild(li" + depth + ");"); // 親要素へ追加 eval("li" + (depth - 1) + ".appendChild(ul" + depth + ");"); eval("ul" + (depth - 1) + ".appendChild(li" + (depth - 1) + ");"); } lastHtag = currentHtag; } } // for // 第一階層へリストを追加 ul1.appendChild(li1); // コンテナ書き出し var container; (function() { container = document.createElement('div'); container.setAttribute("class", "midasi-index-container"); // id属性を設定 var setIdName = uniqueness + "-index"; var matchId = postBody.querySelectorAll('ul[id^="'+setIdName+'"]'); container.setAttribute("id", setIdName +'-'+ matchId.length); postBody.insertBefore(container, scpt.nextSibling); })(); // 目次を書き出し container.insertBefore(navlist, container.firstChild); // タイトルを書き出し if(title)writeTitle(title); function writeTitle(write) { var el = document.createElement('div'); el.setAttribute("class", "midasi-index-title"); el.innerHTML = title; container.insertBefore(el, container.firstChild); } } //sub core }; //sub load } //</script>