JavaScript: 要素内のHタグをリストし目次を自動生成 ページ内リンクを作成する

Quick Edit Pencil
Bloggerで見出しを自動インデックス

今ならもっとスマートなコードを書けると思います。これは2007年頃に書いたコード。2019年に手直ししてますがコードはグダグダです。が動きます。

投稿へ設置


  1. <script>midashi_generator();</script>

レイアウトでガジェットのHTMLをヘッダエリアに追加


  1. //<script type="text/javascript">
  2.  
  3. function midashi_generator(undefined) {
  4.  
  5. // ▼------ 設定項目 ここから ------▼
  6. // --- 以下必須指定 ---
  7.  
  8. // 見出しリスト(目次)生成する文字列の親要素を指定(Bloggerで使用する場合は変更不要)
  9. var midasi_generation_element = "div[id^='post-body-']";
  10. /* Bloggerテンプレートの場合はポスト本文のコンテナ要素 div#post-body-{postId}
  11. セレクターの指定方法は下記URLを参照
  12. Document.querySelectorAll() https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll
  13. 属性セレクタ https://developer.mozilla.org/ja/docs/Web/CSS/Attribute_selectors */
  14. // --- 以下オプション指定 ---
  15.  
  16. // 目次を出すページタイプを指定
  17. var pageType = new RegExp("item|static_page|index|archive");
  18. // アイテムページとスタティックページの時に動く
  19.  
  20. // 目次のタイトル
  21. var title = "目次";
  22.  
  23. // 見出しタグの目次化の指定
  24. /* 半角数値で指定
  25. 指定がない時は全部の見出しタグをリスト
  26. "2-4" とした時は タイトル (H2) と 大見出し (H3) と 小見出し (H4) をリスト
  27. "1|3" とした時は ページタイトル (H1) と 大見出し (H3) をリスト */
  28. var choiceHtags = "";
  29.  
  30. // 目次のリストスタイル指定
  31. /* 半角数値で指定 */
  32. var style1 = ""; // 第一階層(親)
  33. var style2 = ""; // 第二階層(子)
  34. var style3 = ""; // 第三階層(孫)
  35. var style4 = ""; // 第四階層(ひ孫)
  36. // 半角英数で指定
  37. // 指定がない時はデフォルトのリストスタイルが適用されます。
  38. // 下記【】内を指定するとリストスタイル(ulタグ又はolタグ)が適用されます
  39. // マークなし【none】ul
  40. // ・ 記号【disc】ul
  41. // 1 2 3…【decimal】ol
  42. // A B C…【alpha1】ol
  43. // a b c…【alpha2】ol
  44. // Ⅰ Ⅱ Ⅲ... 【roman1】ol
  45. // ⅰ ⅱ ⅲ... 【roman2】ol
  46. // 上記【】内文字に続き半角空白の後に任意の文字列class名を指定することができます。
  47.  
  48. // ▲------ 設定項目 ここまで ------▲
  49.  
  50. // STEP0 必須設定項目の確認
  51. if (!midasi_generation_element) {
  52. info("設定変数の midasi_generation_element が指定されていません。");
  53. return;
  54. }
  55.  
  56. // 目次を出すページタイプを検証(ボディクラスをチェック)
  57. //console.log("body.className = " + document.body.className + " /ボディCLASS属性");
  58. if (!pageType.test(document.body.className)) {
  59. //console.log("midashi-generator.js: 指定したページタイプ以外なのでスクリプトを中止しました。");
  60. return;
  61. }
  62.  
  63. // 当スクリプトを変数にセット
  64. var scpt = document.getElementsByTagName("script");
  65. scpt = scpt[scpt.length - 1];
  66.  
  67. // post-body-postIdを取得
  68. var postBody = getClosestElement(scpt, midasi_generation_element); //console.log(postBody);
  69. if (!postBody) {
  70. info("見出し生成の範囲を特定できませんでした。見出し生成の範囲の指定を確認してください。");
  71. return;
  72. }
  73. var postid = postBody.id.split('-')[2]; /* postIdを取得 */
  74. //console.log('postid '+postid);
  75. var uniqueness = postid; /* 識別子を調整 */
  76. //console.log("uniquenessの完成 " + uniqueness);
  77. function getClosestElement(elem, selector) { /* 条件に合う最も近い祖先要素を取得関数 */
  78. var matchElems = document.querySelectorAll(selector);
  79. if(!matchElems.length){return null;}
  80. //console.log("bingo");
  81. var ancestorNode = elem.parentNode;
  82. while (ancestorNode) {
  83. for (var i = 0; i < matchElems.length; i++) {
  84. if (matchElems[i] === ancestorNode) return ancestorNode;
  85. }
  86. ancestorNode = ancestorNode.parentNode;
  87. }
  88. return null;
  89. };
  90.  
  91. // ページの構築が完了したらloadを呼び出す
  92. (function() {
  93. if (document.addEventListener) { // opera,safari,mozilla向け
  94. document.addEventListener("DOMContentLoaded", load, false);
  95. //console.log("addEventListenerでloadをコールした");
  96. } else if (/msie/.test(userAgent)) { // IE向け
  97. try {
  98. document.documentElement.doScroll("left");
  99. } catch (error) {
  100. setTimeout(arguments.callee, 0);
  101. return;
  102. }
  103. load();
  104. } else { // その他
  105. window.onload = load;
  106. }
  107. })();
  108.  
  109. // 中断メッセージ
  110. function info(message) {
  111. var el = document.createElement('span');
  112. el.setAttribute("style", "background-color: yellow;");
  113. el.innerHTML = "midashi-generator.js: Check the error details on the console.";
  114. scpt.parentNode.insertBefore(el, scpt.nextSibling);
  115. console.error('midashi-generator.js: '+message);
  116. }
  117. // ロードファンクション
  118. function load() {
  119. //console.log("loadを開始した");
  120. // STEP1
  121.  
  122. //対象記事の読み込みを確認する
  123. if (!postBody) {
  124. var tID1 = null;
  125. var tID1i = 0;
  126. var tID1 = setInterval(function() {
  127. if (!postBody || typeof postBody == "undefined") {
  128. if (postBody && postBody.lastChild) {
  129. //console.log('被メニュー要素の中に何か表示されました');
  130. core(); // ナビゲーションメニューの作成
  131. clearInterval(tID1);
  132. } else {
  133. tID1i++;
  134. if (tID1i > 10) {
  135. ul1.innerHTML = "<li>設定された要素のid属性名【" + postBody.id + "】が存在しません。</li><li>要素のid属性名を再確認してください。</li>";
  136. clearInterval(tID1);
  137. }
  138. }
  139. }
  140. }, 300);
  141. } else {
  142. core(); // ナビゲーションメニューの作成
  143. }
  144.  
  145. // STEP2
  146.  
  147. function core() {
  148.  
  149. // STEP3
  150.  
  151. // アーカイブページで次のページが設定されている場合は中止する
  152. if (/index|archive/.test(document.body.className)) {
  153. //console.log("midashi-generator.js: アーカイブページで jump-link があるのでスクリプトを中止しました。");
  154. var postOuter = getClosestElement(postBody, '.post-outer');
  155. if(postOuter.querySelector(".jump-link")){
  156. //console.log(postOuter.querySelector(".jump-link"));
  157. return;
  158. }
  159. }
  160.  
  161. // ナビゲーションメニューを展開する要素を作る/一階層
  162. // メニューを展開する第一階層のul要素をクリエイト
  163. // class属性を設定
  164. if (!style1 || typeof style1 == "undefined") {
  165. var navlist = document.createElement('ul');
  166. //navlist.setAttribute("class", "default");
  167. } else if (/none|disc/.test(style1)) {
  168. var navlist = document.createElement('ul');
  169. navlist.setAttribute("class", style1);
  170. } else if (/decimal|alpha[1-2]|roman[1-2]/.test(style1)) {
  171. var navlist = document.createElement('ol');
  172. navlist.setAttribute("class", style1);
  173. } else {
  174. var navlist = document.createElement('ul');
  175. navlist.setAttribute("class", style1);
  176. }
  177.  
  178. // メニューを展開する第一階層のul要素を変数にセット
  179. var ul1 = navlist;
  180.  
  181. // 結果:<ul class="default" />
  182. // 被メニュー要素のリスト
  183. //console.log("postBody.id " + postBody.id);
  184. // 収集見出しの指定
  185. if (!choiceHtags || typeof choiceHtags == "undefined") {
  186. choiceHtags = "1-4";
  187. }
  188. // セレクターで見出しタグを集める
  189. var headings = postBody.querySelectorAll("h1,h2,h3,h4");
  190. // 一階層リストと二階層以降
  191. var style, currentHtag, depth, lastHtag, a, li1, ul2, li2, ul3, li3, ul4, li4;
  192. lastHtag = 0;
  193. depth = 1;
  194. for (var i = 0; i < headings.length; i++) {
  195. if (RegExp("h[" + choiceHtags + "]").test(headings[i].tagName.toLowerCase())) {
  196. //console.log("Core for Hタグ検出");
  197. // カレントの見出しタグ番号を取得
  198. currentHtag = headings[i].tagName.toLowerCase().replace("h", "");
  199. //console.log("currentHtag " + currentHtag);
  200. // アンカー処理:見出しタグにidを追加
  201. headings[i].id = "mg_" + postid + "_" + i;
  202. // アンカー処理:メニューリンク要素追加
  203. a = document.createElement('a');
  204. a.setAttribute("href", "#mg_" + postid + "_" + i);
  205. //a.setAttribute("onclick", "mg_SmoothScroll(this, '#mg_" + postid+i+"');return false;")
  206. a.textContent = headings[i].textContent;
  207. // 深度(二回以降)
  208. //console.log("lastHtag " + lastHtag);
  209. if (lastHtag > 0) {
  210. depth = depth + (currentHtag - lastHtag);
  211. }
  212. if(depth == 0){
  213. // 構造階層が壊れているときエラーメッセージを出す
  214. info("見出しタグのツリー構造が成立しませんでした。見出し「"+ headings[i].innerText + "」は<" + headings[i].tagName.toLowerCase() + ">ですが、<h" + lastHtag + ">であるべきです。");
  215. headings[i].setAttribute("style", "background-color: deepskyblue!important;");
  216. headings[i].innerHTML = "<span style='color: white!important; background: black;display: block!important;'>midashi-generator.js: !!! You need to set &lt" + headings[i].tagName.toLowerCase() + "&gt to &lth" + lastHtag + "&gt.</span><br />" + headings[i].textContent;
  217. return;
  218. }
  219. //console.log("depth " + depth);
  220. // カレントの指定スタイルをセット
  221. eval("style=style" + depth + ";");
  222. if (depth == 1) {
  223. // 第一階層のリストをセット
  224. //console.log("第一階層 depth " + depth);
  225. li1 = document.createElement('li');
  226. li1.appendChild(a);
  227. //console.log("lastHtag " + lastHtag);
  228. //if (lastHtag == 0) {
  229. ul1.appendChild(li1);
  230. //}
  231. } else {
  232. // 二階層目以降のカレントul/olをセット
  233. //console.log("二階層目以降 depth " + depth);
  234. if ((currentHtag - lastHtag) == 1 || (currentHtag - lastHtag) <= 0) {
  235. if (!style || typeof style == "undefined") {
  236. eval("ul" + depth + "=document.createElement('ul');");
  237. } else if (/none|disc/.test(style)) {
  238. eval("ul" + depth + "=document.createElement('ul');");
  239. eval("ul" + depth + ".setAttribute('class', '" + style + "');");
  240. } else if (/decimal|alpha[1-2]|roman[1-2]/.test(style)) {
  241. eval("ul" + depth + "=document.createElement('ol');");
  242. eval("ul" + depth + ".setAttribute('class', '" + style + "');");
  243. } else {
  244. eval("ul" + depth + "=document.createElement('ul');");
  245. eval("ul" + depth + ".setAttribute('class', '" + style + "');");
  246. }
  247. }else{
  248. // 構造階層が壊れているときエラーメッセージを出す
  249. info("見出しタグのツリー構造が成立しませんでした。見出し「"+ headings[i].innerText + "」は<" + headings[i].tagName.toLowerCase() + ">ですが、<h" + depth + ">であるべきです。");
  250. headings[i].setAttribute("style", "background-color: lightpink!important;");
  251. headings[i].innerHTML = "<span style='color: white!important; background: black;display: block!important;'>midashi-generator.js: !!! You need to set &lt" + headings[i].tagName.toLowerCase() + "&gt to &lth" + depth + "&gt.</span><br />" + headings[i].textContent;
  252. return;
  253. } // カレントliをセット
  254. eval("li" + depth + "=document.createElement('li');");
  255. eval("li" + depth + ".appendChild(a);");
  256. eval("ul" + depth + ".appendChild(li" + depth + ");");
  257. // 親要素へ追加
  258. eval("li" + (depth - 1) + ".appendChild(ul" + depth + ");");
  259. eval("ul" + (depth - 1) + ".appendChild(li" + (depth - 1) + ");");
  260. }
  261. lastHtag = currentHtag;
  262. }
  263. } // for
  264. // 第一階層へリストを追加
  265. ul1.appendChild(li1);
  266.  
  267. // コンテナ書き出し
  268. var container;
  269. (function() {
  270. container = document.createElement('div');
  271. container.setAttribute("class", "midasi-index-container");
  272. // id属性を設定
  273. var setIdName = uniqueness + "-index";
  274. var matchId = postBody.querySelectorAll('ul[id^="'+setIdName+'"]');
  275. container.setAttribute("id", setIdName +'-'+ matchId.length);
  276. postBody.insertBefore(container, scpt.nextSibling);
  277. })();
  278.  
  279. // 目次を書き出し
  280. container.insertBefore(navlist, container.firstChild);
  281.  
  282. // タイトルを書き出し
  283. if(title)writeTitle(title);
  284. function writeTitle(write) {
  285. var el = document.createElement('div');
  286. el.setAttribute("class", "midasi-index-title");
  287. el.innerHTML = title;
  288. container.insertBefore(el, container.firstChild);
  289. }
  290.  
  291. } //sub core
  292.  
  293. }; //sub load
  294.  
  295. }
  296.  
  297. //</script>

参考リンク


このブログの人気の投稿

PowerShellのGetDetailsOf メソッドでプロパティの詳細情報のID番号と項目名を列挙します

Blender: 辺の長さを数値で指定するアドオン

書字方向 横書方向変換スクリプト 左書きから右書きへ(コピペ用途)

ヤバい!JAPANNEXT WQHD対応 27型ワイド液晶ディスプレイ「JN-IPS2716WQHD」を注文してみた!!!

決定版 Edgeでローカルファイルを開く パワーシェルスクリプト (ウインドウズでドロップレット)

PowerShellで複数ファイルのプロパティを取得する方法(準備編)

カルチャーラジオ 科学と人間 薬と毒の歴史をひも解く 日本薬科大学教授…船山信次

Windows10でアプリケーションごとに輝度を設定、自動で変更する - AutoHotkey