Bloggerブログに目次の自動生成のJavaScriptプログラムを実装する 結城永人 - 2023年3月4日 (土) 一般サイト向けの目次の自動生成のJavaScriptプログラムを公開してBloggerブログでも使えるので、どうすれば実装できるかを説明する。 JavaScriptでコンテンツの目次を自動生成して表示するプログラム どのような目次なのかやデザインや機能のカスタマイズについては一般サイト向けの記事を参考にして欲しい。 僕が提供するBlogger用のImaginaryテーマはサンプルブログの自動生成の目次のカスタマイズの記事に専用のソースコードと実装方法を紹介している。 目次の自動生成のプログラムのソースコード ブログのテンプレートか投稿にscriptで、プログラムのソースコードを記載して使う。 /* Copyright: Nagahito Yuki 2023 | https://www.nagahitoyuki.com/2023/03/implement-a-javascript-program-that-automatically-generates-a-table-of-contents-on-a-blogger-blog.html | License: The MIT License */ const pb = document.querySelector("div.post-body"), hdgs = pb.querySelectorAll("h1, h2, h3, h4"); if (hdgs[0]) { const toc = document.createElement("nav"), dts = document.createElement("details"), smr = document.createElement("summary"), fid = document.createElement("ol"); toc.id = "toc"; dts.open = true; smr.insertAdjacentHTML("afterbegin", "目次"); fid.className = "mdr"; dts.appendChild(smr); dts.appendChild(fid); toc.appendChild(dts); let n; hdgs.forEach((hdg, i) => { const tci = document.createElement("li"), tcl = document.createElement("a"), tn = Number(hdg.tagName.substring(1)); hdg.id = `content_${i + 1}`; tcl.href = `#${hdg.id}`; tcl.textContent = hdg.textContent; tci.appendChild(tcl); if (i !== 0 && tn > 1) { const lid = [...fid.querySelectorAll("li")].pop(), dr = tn - n; if (dr > 0) { const idx = document.createElement("ol"); idx.appendChild(tci); lid.appendChild(idx); } else if (dr === 0) { lid.after(tci); } else { let pol = lid; for (c = 0; c + dr < 0; c++) { if (!pol.closest("ol").classList.contains("mdr")) pol = pol.closest("ol").parentElement; else c = -dr; } pol.after(tci); }} else { fid.appendChild(tci); } n = tn; }); hdgs[0].before(toc); } 先頭のコメント(/*〜*/)が著作権を示しているので、無料で使用するかぎり、必ず残して置かなくてはならないし、ソースコードを編集するときなども誤って消さないようにして欲しい。 僕が提供する他の著作権付きプログラムやBlogger用のImaginaryテーマを使っている人はもう既にサイトのソースコードに著者名が記載されているからさらに追加する必要はなくて消しても構わない。 今回の目次の自動生成のプログラムは記事/追加ページの本文の四種類の見出しを取り込んで目次の項目に階層化して最初の見出しの直前に表示する。 Bloggerの投稿の見出しとタグの対応 投稿|Blogger 投稿の作成ビューには四つの見出しが用意されている。 主見出しh1見出しh2小見出しh3準見出しh4 今回のプログラムの初期設定では記事/追加ページの本文で、どれかの見出しが使われたときに取り込んで目次の項目として見出しへのジャンプリンク付きで表示する。 見出しのidの上書きと一般サイト向けとの違い 目次の項目から見出しへのジャンプリンクを作成するために対象の見出しタグ、初期状態ではh1とh2とh3とh4には固有のidが付けられることになる。 注意:目次の項目のジャンプリンクを作成するために見出しのidを見出しの順番で新しく付けていて既存のものがあっても上書きしてしまう。 見出しの既存のidを保持して目次のジャンプリンクにも使うには上記のソースコードのhdg.id = `content_${i + 1}`;をif (!hdg.id) hdg.id = `content_${i + 1}`;に書き換えて使う。 その他のカスタマイズは一般サイト向けの記事に載せている。 Bloggerでソースコードを扱う場合、テンプレートの編集だとXMLだから使うとエラーになる記号があるから注意しなくては行けない。 上記のソースコードはJavaScriptの「<」と「>」の一部と全ての「&」を文字実体参照の「<」と「>」と「&」に書き換えているので、一般サイト向けのソースコードとは少しだけ違う。 目次の自動生成のプログラムの実装方法 目次の自動生成のプログラムはscriptタグで囲ってブログのHTMLに記載する。 <script> 目次の自動生成のプログラムのソースコード </script> 記事/追加ページの本文のHTMLの親ボックスがプログラムの冒頭の("div.post-body")で、つまりpost-bodyのclassを持つdivタグと指定されていてこれが合わないと動作しない。 公式テーマは全て大丈夫で、多くの非公式テーマも同じだろうけど、もしも異なっていたらプログラムの「div.post-body」を相応しいものに書き換える必要がある。 書き換える際は「タグ名#id」「タグ名.class」などのCSSのセレクターと同じ書式で行う。 投稿に個別に使うときは投稿画面のHTMLビューで、目次に取り込みたい全ての見出しの後に目次のscriptを記載すれば動作する。 注意:HTMLビューでは目次の自動生成のソースコードのXMLのための二つの「&」を「&」、一つの「<」を「<」、三つの「>」を「>」に書き換えて使用する。 ブログのテンプレートのソースコードに目次のscriptを記載すれば一回で、全ての記事/追加ページに見出しがあるかぎり、目次が自動生成されて表示できるようになる。 テンプレートにソースコードを記載する テーマ|Blogger Bloggerの管理画面のテーマのメニュー(▼)の「HTMLを編集」か「バックアップ」と「元に戻す」からテンプレートxmlファイルに目次のscriptを記載する。 テンプレートのソースコードに目次のscriptを記載するならば記事/追加ページにしか必要ないので、Bloggerの条件分岐の独自タグのifで他の種類のページで使わないように振り分けた方が無駄がない。 <b:if cond='data:view.isSingleItem'> 目次のscript </b:if> 加えて記事ページと追加ページのどちらかしか目次を使わないという設定も可能になる。 cond属性の内容を変える。 記事ページの振り分け条件data:view.isPost追加ページの振り分け条件data:view.isPage 元のソースコードの「data:view.isSingleItem」(記事/追加ページ)となっている振り分け条件を書き換えると記事ページか追加ページに目次の使用を限定できる。 どこに記載するかは目次の表示速度を気にしなければテンプレートのソースコードの最後、bodyの閉じタグの</body>の直前が分かり易くて動作も確実だけど、今回のプログラムは最初の見出しの直前に目次を挿入するので、初回画面に入る位置で、もしも遅れると次のコンテンツを押し下げる違和感を与え兼ねないと懸念される。 普通の仕方で、表示速度をなるべく上げるには目次に取り込む見出しのある本文の直ぐ後に目次の自動生成のソースコードを置いて開始できるようにするのが良いと思うので、紹介しておく。 記事/追加ページの本文の直後に目次のscriptを記載する 公式テーマだと「post-body entry-content」のclassが付いたdivが本文の親ボックスになっているので、その直後に目次のscriptを記載する。 <div class='post-body entry-content'> (中略) </div> 目次のscript divには他の属性が付いていたり、中身も異なるので、テンプレートのソースコードによって変わるけれどもclass='post-body entry-content'に着目すれば間違いない。 注意:Bloggerのレイアウトバージョン2のテンプレートは本文のソースコードがパソコンとスマホの二つに分かれている。 テンプレートのソースコードの冒頭のhtmlタグにb:version='2'が付いているとレイアウトヴァージョン2となる。 公式テーマだと七種類が含まれる。 SimpleDynamic ViewsPicture WindowAwesome Inc.WatermarkEtherealTravel 七つのテーマはパソコン版/デスクトップ表示の本文に目次のscriptを追加してテンプレートを管理画面のテーマの「モバイルの設定」でスマホにも対応させる場合はスマホ版/モバイル表示の本文にもscriptを追加するようになる。 レイアウトヴァージョン2のパソコン版の本文のソースコード <b:includable id='post' var='post'> (中略) <div class='post-body entry-content' expr:id='"post-body-" + data:post.id' expr:itemprop='(data:blog.metaDescription ? "" : "description ") + "articleBody"'> <data:post.body/> <div style='clear: both;'/> <!-- clear for photos floats -- </div> 目次のscript (中略) </b:includable> レイアウトヴァージョン2のスマホ版の本文のソースコード <b:includable id='mobile-post' var='post'> (中略) <div class='post-body entry-content' expr:id='"post-body-" + data:post.id' itemprop='articleBody'> <data:post.body/> <div style='clear: both;'/> <!-- clear for photos floats --> </div> 目次のscript (中略) </b:includable> Bloggerの非公式テーマもレイアウトヴァージョン2だと本文のソースコードかパソコンとスマホの二つの分かれているかも知れない。 目次のscriptを本文の直後に置いても本文に遅れて目次か挿入される時間差を全て取り除くことはできないので、どうしても気がかりな場合は一般向けの記事に載せた完全な回避策を取ると良いと思う。 投稿画像のカスタマイズに組み込む 僕が提起した投稿画像の遅延読み込みのloading-lazyとIntersection Observer API、または軽量化のWebPのカスタマイズを実装してちるテンプレートはそこに目次の自動生成のプログラムを組み込むと目次を画像以外のコンテンツとして他のものと一緒に表示できるから通常の本文との表示の時間差を解消できる。 注意:投稿画像のソースコードが画像なしのページでも記載されて部分的に使用されるので、全てのページで本文自体の表示速度が僅かに――プログラムの処理の分だけ――下がることになる。 目次の自動生成のプログラムの投稿画像のプログラムへの組み込みは三つの手順で行われる。 画像用の二つの振り分けタグを外すソースコードの最初の方を編集する画像なしのページの処理を追加する 三番目の手順は投稿画像のソースコードが動画を除外するものになっていたらそこに必要な目次の処理も含まれるから行わなくて良い。 画像用の二つの振り分けタグを外す 目次は画像なしのページでもあり得るので、全てのページで投稿画像のプログラムを記載して画像なしのページでは部分的に使用する。 ソースコードの三箇所を削除する。 <b:tag class='blog-content' cond='data:view.featuredImage' name='noscript'><data:post.body/></b:tag> <b:if cond='!data:view.featuredImage'> 投稿画像のソースコード </b:if> ※打ち消し線が不要な部分。 cond属性の内容は使い方によって違う場合もあるかも知れないけれどもどんな内容でもtagタグの方はcond属性だけ消してifタグの方はタグ自体を外す。 ソースコードの最初の方を編集する 投稿画像のソースコードの最初の方に目次のソースコードを追加する。 元のソースコード const blimgs = npsbd.querySelectorAll("img"); 新しいソースコード const blimgs = npsbd.querySelectorAll("figure:not(.normal) img, iframe"), hdgs = npsbd.querySelectorAll("h1, h2, h3, h4"); if (hdgs[0]) { const toc = document.createElement("nav"), dts = document.createElement("details"), smr = document.createElement("summary"), fid = document.createElement("ol"); toc.id = "toc"; dts.open = true; smr.insertAdjacentHTML("afterbegin", "目次<span/>"); fid.className = "mdr"; dts.appendChild(smr); dts.appendChild(fid); toc.appendChild(dts); let n; hdgs.forEach((hdg, i) => { const tci = document.createElement("li"), tcl = document.createElement("a"), tn = Number(hdg.tagName.substring(1)); hdg.id = `content_${i + 1}`; tcl.href = `#${hdg.id}`; tcl.textContent = hdg.textContent; tci.appendChild(tcl); if (i !== 0 && tn > 1) { const fis = fid.querySelectorAll("li"), lid = fis[fis.length - 1], dr = tn - n; if (dr > 0) { const idx = document.createElement("ol"); idx.appendChild(tci); lid.appendChild(idx); } else if (dr === 0) { lid.after(tci); } else { let pol = lid; for (c = 0; c + dr < 0; c++) { if (!pol.closest("ol").classList.contains("mdr")) pol = pol.closest("ol").parentElement; else c = -dr; } pol.after(tci); }} else { fid.appendChild(tci); } n = tn; }); hdgs[0].before(toc); } 投稿画像のソースコードの("img")の部分の内容は使い方によって異なる場合がある。そのままで構わなくて半角コンマ(,)で繋いで目次のソースコードの少し加工したものを追加する。 目次のソースコードの直後には投稿画像のソースコードが来て使い方によって異なるけど、if (blimhs[0])かそれを外した次の行のPromise.allになると思う。 ソースコードの最後の方を編集する 投稿画像のソースコードの最後に目次のソースコードを少し追加する。 最初の方に動画を除外する振り分けのif (blimgs[0])が付いていれば不要で、ない場合に新しく追加して記事/追加ページに画像がない場合でも見出しかあれば目次を表示できるようにする。 if (blimgs[0]) { Promise.all([...blimgs].map(blimg => { (中略) // Intersection Observer APIのタイプ if ("IntersectionObserver" in window) postContentObserver.observe(result); else result.src = result.dataset.src; }); }); // lazy-loadingかrwのタイプ psbd.parentNode.replaceChild(npsbd, psbd); }); } else { psbd.parentNode.replaceChild(npsbd, psbd); } ※太字が追加する部分。 投稿画像のソースコードのタイプによって二つ目に追加する新しいソースコードの直前の状況が違うけど、とにかくPromise.allのところから最後までをif (blimgs[0]) {と} else { psbd.parentNode.replaceChild(npsbd, psbd); }で囲むと画像なしのページの目次の処理が可能になる。 目次のデザインのソースコード ブログに実装した目次の自動生成のプログラムに合わせて投稿かテンプレートのCSSに記載する。 #toc>details {background-color:rgba(176, 196, 222, 0.02);border:1px #b0c4de outset;padding:1em} #toc>details>summary {list-style-type:none;display:flex;justify-content:space-between} #toc>details>summary>span {text-decoration:dotted underline .125em} #toc>details>summary>span::before {content:'開く'} #toc>details[open]>summary>span::before {content:'閉じる'} .mdr {margin-bottom:0} .mdr ol {padding-left:1.25em} .mdr li {margin:4px auto} 目次に枠線や開閉ボタンなどの初期のデザインを付けられる。どんな内容か、そしてカスタマイズに使えるCSSの指定先などについては一般サイト向けの記事に載せている。 目次のデザインの追加 BloggerでCSSを使うには主に三つの仕方が考えられる。 投稿の編集画面のHTMLビューテーマのカスタマイズのCSSを追加テーマのxmlファイルの編集 それぞれの特徴と実装方法について取り上げる。 投稿の編集画面のHTMLビュー 目次を個々の投稿に追加するために投稿の編集画面に目次のscriptを記載した場合はデザインのCSSも目次のない他のページには必要ないからプログラムと一緒に記載すると良いと思う。 JavaScriptで目次の自動生成のプログラムにデザインのCSSを組み込んで使うことができる。 document.head.querySelector("style").insertAdjacentHTML("beforeend", "改行なしの目次のCSS"); このソースコードを目次の自動生成のプログラムの冒頭のconst pbの直前などに記載すると目次のCSSも反映する。 注意:プログラムに追加するCSSのソースコードは改行せず、全てが繋がって一行になるように加工しておかないとエラーで目次が表示されなくなる。 CSSに変更を加えたいときもプログラムの中で、編集することができる。 テーマのカスタマイズのCSSを追加 カスタマイズ|Blogger 目次をテンプレートに追加した場合はデザインのCSSも全ての記事/追加ページに追加する必要があってテーマのカスタマイズのCSSを追加が適している。 Bloggerの管理画面のテーマのカスタマイズの詳細設定の「CSSを追加」を使うとテンプレートに新しいCSSのソースコードを記載できる。 入力欄に目次のソースコードを入力して保存する。 テーマのxmlファイルの編集 目次をテンプレートに追加した場合はデザインのCSSも全ての記事/追加ページに追加する必要があってテーマのxmlファイルの編集が適している。 Bloggerの管理画面のテーマのメニュー(▼)の「HTMLを編集」か「バックアップ」と「元に戻す」を使うとテンプレートに新しいCSSのソースコードを記載することができる。 目次のscriptを記載する場合と同じだ。 <b:skin><![CDATA[/* (中略) 目次のCSS ]]></b:skin> Bloggerの公式テーマと多くの非公式テーマで、head内にskinタグがあってHTMLのstyleに変換される部分なので、目次のCSSを他の振り分けのCSSに紛れ込まないように閉じタグの末尾の]]></b:skin>の直前などに記載すると良いと思う。 万一、skinがなければstyle、styleもなければ投稿の編集画面のHTMLビューと同じように目次のscriptに組み込んでも構わない。 コメント 新しい投稿 前の投稿
細川慎二のどうにも泣けて来てしまうストリートライヴのアルトサックス サックス奏者の 細川慎二 のYouTubeチャンネルの Sax in the Night City で出ているサックスのストリートライヴの動画が美しい音色と相俟った街角の雰囲気の良さで心底と泣けて来るほどの感動を催させる。 細川慎二のアルトサックスの美しい音色が響き渡る街角の...
宜保愛子は本物の霊能力者だと考えられる三つの真実 昭和から平成にかけてテレビや雑誌で何度も大きく取り上げられて物凄く活躍した霊能力者の 宜保愛子 がいた。何気なく昔のテレビ番組を観ていたら霊視は嘘だったと思えない内容で、本当にびっくりした。昔、そんなに引き付けられて観ていたわけではないし、改めて霊能力が本当かどうかを確かめようと...
平田監督の白井球審の誤審への抗議はパワハラへの強力な対処法に他ならない 日本プロ野球で 佐々木朗希が完全試合を実現して 次の試合も八回まで無安打と無失点の状況で、次の試合はどうかと注目した4月24日の対オリックスバファローズ戦は初回の先頭打者の初球にヒットを打たれて五回に二失点を喫して連続の無安打と無失点が両方とも途絶えてしまった。 しかし予想外...
アドセンスのGDPRに日本でも対応しなくてはならない場合がある アドセンスの個人情報のCookieなどの使用に関してサイトの訪問者に同意を得なくてはならない法律としてEU(European Union/欧州連合)の GDPR (General Data Protection Regulation/EU一般データ保護規則)がある。外国の個人情報...
伊良部秀輝が自殺した原因はミッドライフクライシスによる鬱と飲酒だと考える プロ野球選手の 伊良部秀輝 が自殺したと知ってショックを受けたことがあった。もう十年以上前になる。2011年の夏、享年四十二と早過ぎたのに加えて大好きな投手の一人だったので、とても残念に感じた。 目次 伊良部秀輝が大好きだった記憶 負けても自分のスタイルを貫き通した 野球への...
日本人がジャニーズ事務所で行われた性加害よりも恐れていること イギリスの公共放送のBBC(British Broadcasting Corporation/英国放送協会)が日本のジャニーズ事務所の創業者の ジャニー喜多川 の性加害について取り上げたドキュメンタリーの J-POPの捕食者:秘められたスキャンダル に衝撃を受けた。 目次 ジ...
玉置浩二のメロディーは涙腺緩んで総毛立つ名曲中の名曲だ 玉置浩二 のYouTubeの公式チャンネルで、最も気に入りの メロディー のライブ版が追加されていた。曲自体をちゃんと聴いたのは約二十五年振りかも知れないけど、しかし初めての内容から以前にも況して大変な感動を覚えることになった。 玉置浩二 『メロディー』Live at Tok...
沖雅也の涅槃への自殺と双極性障害 かつて俳優の 沖雅也 の自殺が伝えられたとき、遺書の言葉とされた「おやじ、涅槃でまっている」と共に何なのかと疑問を感じたのを良く覚えている。聞き慣れない「涅槃」という言葉が入っても何十年も過ぎた今振り返っても自殺者の遺書として本当に珍しい表現だったと改めて驚く。 沖雅也が書い...
Imgurで画像URLと埋め込みコードを取得する方法 Imgur は自分でアップロードした画像については画像URL/直リンクを取得して他のサイトにHTMLのimgタグで表示させられる。 そして自分と他の人たちも含めて画像の埋め込みコードを取得して他のサイトのHTMLに、そのまま、記載して表示させられもする。 目次 Img...
Googleの誕生祝いでプロフィールアイコンに風船と紙吹雪が飛んで来た サイトを見ていたら妙な感じがするので、良く見てみるとGoogleサービスで、ログインして右上のプロフィールアイコンが出ているときに風船と紙吹雪が飛んで来ていることに気付いた。 Googleから久し振りに受け取った誕生祝い サマリー|Search Console| Goog...
コメント