Imaginaryの記事/追加ページの本文の画面の左下に表示される概要ボタンは本文に見出し、HTMLのh1かh2かh3の見出しタグがあった場合にそれを取り込んだ目次を自動生成して表示します。
本稿ではボップアップの目次と本文への自動生成の目次の追加とポップアップのコンテンツのカスタマイズについて紹介します。

カスタマイズは全てBloggerの管理画面の「テーマ」のカスタマイズの右横のメニューボタン(▼)のHTMLを編集かバックアップと元に戻すからテンプレートのソースコードを書き換えます。
追記:Imaginaryのポップアップの目次は2023年4月3日のバージョンから搭載していますので、それ以前のバージョンを使っている場合はポップアップの目次を表示できるバージョンに取り替えて下さい。
ポップアップの目次のソースコード
ポップアップの目次のソースコードについて説明します。
最後のscriptタグのSummary buttonのソースコード
ポップアップの目次を自動生成して表示するためのものです。
/* Summary button */
const pb = Blog1.querySelector("div.entry-content"), hdgs = pb.querySelectorAll("h1, h2, h3");
if (hdgs[0]) {
const dts = document.createElement("details"), smr = document.createElement("summary"), fid = document.createElement("ol");
dts.open = true; smr.insertAdjacentHTML("afterbegin", "見出し"); fid.className = "mdr"; dts.appendChild(smr); dts.appendChild(fid);
let n;
hdgs.forEach((hdg, i) => {
const tci = document.createElement("li"), tcl = document.createElement("a"), tn = Number(hdg.tagName.substring(1));
if (!hdg.id) 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 {
lid.closest("ol").parentElement.after(tci); }} else {
fid.appendChild(tci); }
n = tn; });
const ift = summary_button.firstElementChild;
wfsc.firstElementChild.insertAdjacentElement("afterbegin", dts); ift.classList.add("original-icon"); ift.setAttribute("viewBox", "0 0 24 24"); ift.innerHTML = "<path d='M2 4 H22 M7.3 9.4 H22 M7.3 14.7 H22 M7.3 20 H22'/><circle cx='3.5' cy='9.4' r='0.5'/><circle cx='3.5' cy='14.7' r='0.5'/><circle cx='3.5' cy='20' r='0.5'/>"; }
const aab = wfsc.querySelector("div.smtx").lastElementChild, pau = aab.previousElementSibling;
pau.textContent = `${Blog1.querySelector("a.published>time").textContent} / ${Blog1.querySelector("li.updated>time").textContent}`;
if (Blog1.querySelector(".fn")) aab.insertAdjacentHTML("afterbegin", `${Blog1.querySelector(".fn").textContent} - `);
const cpps = wfsc.querySelectorAll("ol.mdr a");
function scpp() {
wfsc.classList.add("hidden"); cpps.forEach(cpp => cpp.removeEventListener("click", scpp)); }
summary_button.addEventListener("click", () => {
wfsc.classList.toggle("hidden");
if (cpps[0]) cpps.forEach(cpp => cpp.addEventListener("click", scpp)); });
ポップアップの目次は本文の見出しタグのh1とh2とh3を取り込んで自動生成されています。
表題として「見出し」と表示されます。変更したい場合はソースコードの前の方の「見出し」を他の言葉に書き換えて下さい。
目次の項目から見出しのジャンプリンクを付けるために本文の見出しタグのidを取り出してidが付いてないもの(通常の投稿では何も付きません)は新しく付けています。
新しく付けられる見出しタグのidはcontent_見出しの順番
となります。
文字列のcontent_
の部分は他の半角の英数字とハイフン(-)やアンダーバー(_)などのidで使用可能な一部の記号に置き換えても構いません。
見出しのidを全てcontent_見出しの順番
と同じ仕方で纏めたい場合はソースコードのif (!hdg.id) hdg.id = `content_${i + 1}`;
のif (!hdg.id)
を削除すると見出しの元のidを上書きすることができます。
そして("h1, h2, h3")
は取り込む見出しタグの種類で、減らしても動作に支障はありません。
Bloggerの作成ビューの見出しタグ
- 主見出し
- h1
- 見出し
- h2
- 小見出し
- h3
- 準見出し
- h4
Imaginaryの記事/追加ページではページタイトルに見出しのh1タグが付いてますので、本文では見出しのh2から使うとHTMLの全体の構成が良いと思います。
目次に取り込む見出しタグを減らす場合は区切りの半角コンマ(,)を残さないように注意して下さい。残すとプログラムのエラーで、概要ボタンが動作しなくなります。
h1を消した場合、ソースコードのif (i !== 0 && tn > 1)
の一部は不要ですので、消してif (i !== 0)
だけでも構いません。
取り込む見出しタグの種類を増やすこともできます。
h4を追加する場合、ソースコードの取り込む見出しタグの種類のところに「h4」を追加した上で、次のソースコードを変更して下さい。
元のソースコード
lid.closest("ol").parentElement.after(tci);
新しいソースコード
const pol = lcs.parentElement;
if (dr === -1) {
pol.after(tci); } else {
pol.closest("ol").parentElement.after(tci);
if (tci.parentElement.tagName !== "OL") fid.appendChild(tci); }
同じソースコードによって投稿のHTMLビューでh5やh6の見出しタグを使った場合、取り込む見出しタグの種類のところに「h5」や「h6」を追加するとポップアップの目次に取り込むこともできます。
少ない見出しで目次を作る必要がない場合は目次を作り始める見出しの最低数を設定できます。
ソースコードの最初の方の(hdgs[0])
を(hdgs.length >= 最低数)
に変更して下さい。
本文の見出しの数が最低数を含めて越えたときだけ目次が作られるようになります。
一部のページで目次表示を除外する方法
テンプレートにポップアップの目次を搭載すると全ての記事/追加ページに適用されます。
見出しがなければ目次は生成されませんが、見出しがあっても目次を生成したくないページがある場合、最初の見出しのHTMLの見出しタグに何かのclassを付けると共にポップアップの目次表示のソースコードのif (hdgs[0])
の部分をif (hdgs[0] && !hdgs[0].classList.contains("見出しに付けたclass")
に変更します。
見出しのh2にno-tocのclassを付けて除外するならば
編集部分①投稿の編集画面のHTMLビュー
<h2 class="no-toc">見出し</h2>
編集部分②テンプレートの最後のscript。
if (hdgs[0] && !hdgs[0].classList.contains("no-toc")) {...}
※太字が新しく追加する部分。
テンプレートは一回だけ、投稿は目次を作りたくないページがあるときに編集します。
それぞれのclassは半角の英数字とハイフン(-)やアンダーバー(_)などのclassで使用可能な一部の記号に置き換えても構いませんが、ポップアップの目次表示を除外するためには必ず一致させて下さい。
見出しの数を制限したソースコードの場合はhdgs[0]
の代わりのhdgs.length >= 最低数
に同じように&& !hdgs[0].classList.contains("見出しに付けたclass")
を追加します。
ページの種類、記事ページか追加ページで目次の有無を切り替えるならばポップアップの目次表示のソースコードの一部をBloggerの振り分けタグで囲って下さい。
<b:if cond='data:view.isページの種類>
const pb = Blog1.querySelector("div.post-body"), hdgs = pb.querySelectorAll("h1, h2, h3");
(中略)
summary_button.firstElementChild.innerHTML = ""; }
</b:if>
(中略)
※太字が新しく追加する部分。
ソースコードの帯文字の「ページの種類」にポップアップの目次表示を行いたいページを入力します。
- 記事ページ
- Post
- 追加ページ
- Page
当の振り分けタグに入力されなかったページで、ポップアップの目次表示が除外されます。
前半のCSSのフローティングボタン/メッセージのソースコード
ポップアップの目次のデザインを付けるためのものです。
テンプレートの前半のskn内のソースコードがデザインのCSSになっています。
ポップアップの目次のCSSは「フローティングボタン/メッセージ」というコメントのところに纏められています。CSSに同じコメントのところが、複数、ありますが、最初のもので、「SVGアイコン」の次に置かれたソースコードです。
.smct {display:grid;grid-template-columns:repeat(auto-fit, minmax(calc($(content.width / 3) - (var(--floatingbar-height) + var(--fixedbutton-margin) + var(--maincolumn-padding)) * 2), 1fr));gap:1em;font-size:small;padding:var(--maincolumn-padding);background-color:$(body.background.color);color:$(body.text.base.color);border:1px $(sidebar.separator.color) double;box-shadow:5px 5px 10px -5px rgba(0, 0, 0, .4);margin:0;height:min(80dvh - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 3, 480px - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 2);overflow-y:scroll}
details:has(.mdr)>summary {list-style-type:none}
details:has(.mdr) ol {padding-$startSide:1.25em}
details:has(.mdr) li {margin:4px auto}
.mdr {margin-bottom:0}
CSSで指定されている内容は次の通りです。
- .smct
- max-height:min(80dvh - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 3, 480px - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 2)
スマホでポップアップのコンテンツが増えて縦長になっても画面内に十分に収まるようにして最大でも画像の元の横幅(480px)から上下の隙間を引いたところまでに抑えます。 - overflow-y:scroll
スマホでポップアップのコンテンツが増えて縦長になって途切れた部分をスクロールで見えるようにします。
- max-height:min(80dvh - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 3, 480px - (var(--floatingbar-height) + var(--fixedbutton-margin)) * 2)
- details:has(.mdr)>summary
- list-style-type:none
目次の左横のマーカー(▼)を消します。
- list-style-type:none
- details:has(.mdr) ol
- padding-$startSide:1.25em
目次の項目の左の内側の余白を調整します。
- padding-$startSide:1.25em
- details:has(.mdr) li
- margin:4px auto
個々の項目の外側の余白を調節します。
- margin:4px auto
- .mdr
- margin-bottom:0
目次の下の外側の余白を消します。
- margin-bottom:0
初期のCSSの内容を変えたり、新しいCSSを追加したりしてポップアップの目次のデザインをカスタマイズできます。
本文にも目次を表示する方法
テンプレートのソースコードの二箇所を編集します。
最後のscriptタグのSummary buttonを編集する
元のソースコード
/* Summary button */
const pb = Blog1.querySelector("div.entry-content"), hdgs = pb.querySelectorAll("h1, h2, h3, h4");
(中略)
cpp.addEventListener("click", scpp)); });
途中を省略しているので、実際のソースコードはもっと長いです。
本文と共用のポップアップの目次表示のソースコード
新しいソースコード
/* Summary button */
const aab = wfsc.querySelector("div.smtx").lastElementChild, pau = aab.previousElementSibling;
pau.textContent = `${Blog1.querySelector("a.published>time").textContent}/${Blog1.querySelector("li.updated>time").textContent}`;
if (Blog1.querySelector(".fn")) aab.insertAdjacentHTML("afterbegin", `${Blog1.querySelector(".fn").textContent}-`);
if (Blog1.querySelector("nav#toc")) {
wfsc.firstElementChild.insertAdjacentElement("afterbegin", toc.firstElementChild.cloneNode(true)); summary_button.firstElementChild.classList.add("original-icon"); summary_button.firstElementChild.innerHTML = "<path d='M2 4 H22 M7.3 9.4 H22 M7.3 14.7 H22 M7.3 20 H22'/><circle cx='3.5' cy='9.4' r='0.5'/><circle cx='3.5' cy='14.7' r='0.5'/><circle cx='3.5' cy='20' r='0.5'/>"; }
const cpps = wfsc.querySelectorAll("ol.mdr a");
function scpp() {
wfsc.classList.add("hidden"); cpps.forEach(cpp => cpp.removeEventListener("click", scpp)); }
summary_button.addEventListener("click", () => {
wfsc.classList.toggle("hidden");
if (cpps[0]) cpps.forEach(cpp => cpp.addEventListener("click", scpp)); });
元のソースコードを新しいソースコードで置き換えて下さい。
script内の一番最後の「</b:if>});」を消さないようにして下さい。消すと他のボタンなどの全ての機能が止まります。
Blog1のwidgetタグのpostBodyのidのincludableを編集する
<b:includable id='postBody' var='post'>
<!-- If metaDescription is empty, use the post body as the schema.org description too, for G+/FB snippeting. -->
<div class='post-body entry-content' expr:id='"post-body-" + data:post.id'>
<data:post.body/>
</div>
本文の目次表示のソースコード
</b:includable>
本文の目次表示のソースコード
<b:if cond='data:view.isSingleItem'><script>
const pb = Blog1.querySelector("div.entry-content"), hdgs = pb.querySelectorAll("h1, h2, h3");
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));
if (!hdg.id) 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 {
lid.closest("ol").parentElement.after(tci); }} else {
fid.appendChild(tci); }
n = tn; });
hdgs[0].before(toc); }</script></b:if>
二つのソースコードを、それぞれ、所定のscriptタグと所定のincludableタグに追加すると概要ボタンのポップアップの先頭と本文の最初の見出しの直前に目次が表示されるようになり、概要ボタンのアイコンが目次用のものに切り替わります。
本文の目次は単体で使うことができます。その場合、ポップアップの目次のカスタマイズは不要です。
一部のページで目次表示を除外する方法
一部のページで目次表示を除外したい場合、ページを個々に除外する方法はポップアップだけの場合のカスタマイズと全く同じです。
ページの種類で目次表示の有無を切り替える場合は本文の目次表示のソースコードにBloggerの記事/追記ページの振り分けタグの<b:if cond='data:view.isSingleItem'></b:if>
が最初から付いてますので、さらにもう一つの振り分けタグで新しく囲わずに「SingleItem」の部分を目次表示を行いたいページの種類の「Post」(記事ページ)か「Page」(追加ページ)に書き換えて、そのまま、使って下さい。
本文の目次は最初の見出しの直前に表示されます。
目次の内容やその他のカスタマイズについては概要ボタンから目次を表示するに載せたものと変わりません。
前半のskinタグのフローティングボタン/メッセージを編集する
同じメッセージが、複数、ありますが、最初のもので、「SVGアイコン」の次に置かれたソースコードです。
ポップアップの目次のCSSに続けて次のCSSを追加して下さい。
#toc>details {background-color:rgba($(sidebar.separator.color.red), $(sidebar.separator.color.green), $(sidebar.separator.color.blue), 0.02);border:1px $(sidebar.separator.color) outset;padding:1em}
#toc>details>summary {display:flex;justify-content:space-between}
#toc span {text-decoration:dotted underline .125em}
#toc>details>summary>span::before {content:'開く'}
#toc>details[open]>summary>span::before {content:'閉じる'}
CSSで指定されている内容は次の通りです。
- #toc>details
- background-color:rgba($(sidebar.separator.color.red), $(sidebar.separator.color.green), $(sidebar.separator.color.blue), 0.02)
本文の目次の背景色を付けます。 - border:1px $(sidebar.separator.color) outset
本文の目次の枠線を付けます。 - padding:1em
本文の目次の内側の余白を付けます。
- background-color:rgba($(sidebar.separator.color.red), $(sidebar.separator.color.green), $(sidebar.separator.color.blue), 0.02)
- #toc>details>summary
- display:flex
本文の目次の見出しの置き方を整えます。 - justify-content:space-between}
本文の目次のタイトルと開閉ボタンを左右に分けます。
- display:flex
- #toc span
- text-decoration:dotted underline .125em
本文の目次の開閉ボタンに点線の下線を引きます。
- text-decoration:dotted underline .125em
- #toc>details>summary>span::before
- content:'開く'
本文の目次が閉じたときに「開く」と表示します。
- content:'開く'
- #toc>details[open]>summary>span::before
- content:'閉じる'
本文の目次が開いたときに「閉じる」と表示します。
- content:'閉じる'
追加したCSSの内容を変えたり、新しいCSSを追加したりしてポップアップの目次のデザインをカスタマイズできます。
ポップアップのコンテンツのカスタマイズ
ポップアップの目次以外のコンテンツの扱いについて載せます。
attributionのidのsectionタグの直前を編集する
<button class='svg-icon-24-button hidden' expr:aria-label='data:messages.learnMore' id='summary_button' type='button'>
<svg class='svg-icon-24' role='img'><use href='/responsive/sprite_v1_6.css.svg#ic_more_vert_black_24dp'/></svg>
</button>
<div class='hidden' id='wfsc'>
<figure class='smct'>
<b:tag cond='data:view.featuredImage' expr:alt='data:messages.photo' expr:height='252' expr:src='resizeImage(data:view.featuredImage, 480, "1200:630")' loading='lazy' name='img' width='480'/>
<div class='smtx'>
<p><data:view.title.escaped/></p>
<p><data:blog.metaDescription.escaped/></p>
<p><data:view.url.canonical/></p>
<p/>
<p><data:blog.title.escaped/></p>
</div>
</figure>
</div>
このソースコードが概要ボタンとポップアップのコンテンツのソースコードです。
- button id='summary_button'
- 概要ボタン
- div id='wfsc'
- ポップアップの大外の配置枠
- figure class='smct'
- ポップアップの親ボックス
- b:tag name='img'
- ポップアップの画像
- div class='smtx'
- ポップアップの文章の親ボックス
- p/一番目
- ポップアップの記事/付いページのタイトル
- p/二番目
- ポップアップの記事/付いページの検索向け説明
- p/三番目
- ポップアップの記事/付いページのURL
- p/四番目
- ポップアップの記事/付いページの公開日と更新日
- p/五番目
- ポップアップの記事/付いページの著者名とブログ名
最初は画像と五つの文章の集まりが表示されます。
ポップアップのコンテンツの削除について
画像を消したい場合は画像のb:tagを削除します。
文章の集まりを消したい場合は五つのpタグを含めた文章の親ボックスのdivタグと最後のscriptタグの/* Summary button */
のところから次のソースコードを削除します。
const aab = wfsc.querySelector("div.smtx").lastElementChild, pau = aab.previousElementSibling;
pau.textContent = `${Blog1.querySelector("a.published>time").textContent}/${Blog1.querySelector("li.updated>time").textContent}`;
if (Blog1.querySelector(".fn")) aab.insertAdjacentHTML("afterbegin", `${Blog1.querySelector(".fn").textContent}-`);
文章はdivタグだけ削除してscriptタグのソースコードを残しておくとエラーで、概要ボタンが動作しなくなります。
ポップアップのコンテンツの置き替えについて
画像と文章の親ボックスはそれぞれのタグで置き替え可能です。
個々の文章は四番目と五番目以外はpタグだけで置き換え可能です。
四番目の公開日と更新日の文章を動かしたい場合はpタグに何かのclassを付けて先程の削除用のscriptタグのpau = aab.previousElementSibling
をpau = wfsc.querySelector("p.クラス名")
に書き換えて下さい。
五番目の著者名とブログ名の文章を動かしたい場合は四番目の文章の動かすカスタマイズを行った上で――四番目の文章を動かさなくてもプログラムで関連しているために必要です――さらに同じようにpタグに何かのclassを付けて先程の削除用のscriptタグのaab = wfsc.querySelector("div.smtx").lastElementChild
をaab = wfsc.querySelector("p.クラス名")
に書き換えて下さい。
目次はポップアップの先頭に表示されますが、画像や文章の集まりと置き換えたい場合は最後のscriptタグに新しく追加するソースコードを編集して下さい。
ポップアップの最後に目次を表示する
ポップアップだけの目次表示の場合
wfsc.firstElementChild.insertAdjacentElement("beforeend",dts);
ポップアップと本文の目次表示の場合
wfsc.firstElementChild.insertAdjacentElement("beforeend", toc.firstElementChild.cloneNode(true));
※太字が変更する部分。
どちらも元の「afterbegin」がポップアップの先頭の指定で、「beforeend」がポップアップの末尾の指定です。
ポップアップの何れかのコンテンツの次に目次を表示する
ポップアップだけの目次表示の場合
wfsc.querySelector("セレクター").insertAdjacentElement("afterend",dts);
ポップアップと本文の目次表示の場合
wfsc.querySelector("セレクター").insertAdjacentElement("afterend", toc.firstElementChild.cloneNode(true));
※太字が変更する部分。
どちらも元のソースコードの「firstElementChild」と「afterbegin」と書き換えます。
セレクターは置き換える目次の直前に来るものについて入力します。
CSSと同じ書き方で、大丈夫で、ポップアップの画像ならば一つしかないので、「img」、文章の親ボックスならば「div.smct」となります。
ポップアップの親ボックス、文章の親ボックスには色んなコンテンツをHTMLで記載して載せられますが、その場合でもセレクターを個別に指定すれば目次をコンテンツの間に挿入することができます。
コメント