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>