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>