以前にもTOCの記事は書いていますが今回jQuery依存なしのvanilla JS(ネイティブJS, pure JS)のご紹介と説明で統一しようと思います。私が今後制作するテンプレートはよほどのことが無い限りjQueryを入れないと思います。それなのにjQueryで紹介というのはやはりマズいもので。
導入時の注意「見出しレベル」を追記しました。
今回は以前書いた内容に加え、より詳細な説明をしたいと思います。特に難しいわけではないのですが、FC2ブログでの導入 に的を絞ると結構コツというか特徴を押さえておかないと上手くいきません。ついでに複数のCSSスタイリングも用意しました。
以降の内容で「TOCを記載, TOCを掲載」と出てきましたらそれは「見出し目次リストを書き出すhtmlの記載」であり、「TOCが記載」されている場所がイコール「見出し目次リストが表示される場所」だと思ってください。
導入時の注意「見出しレベル」について
とても重要な章なので導入前に必ずお読みください。
導入にあたり 利用中のテンプレートの見出し仕様と、自身の方針をよく検討してください。FC2ブログで配布されているほとんどのテンプレートは 記事内で利用できる見出しの最上位は h3 という仕様です。理由は、FC2ブログのエディターの見出しツールがh3からになっているためです。
- h1 --- ブログタイトル
- h2 --- 記事タイトル
- H3以降 --- 記事内で利用される見出し
こういう形式ですね。ですが私の提供しているテンプレートの場合、 記事内で利用できる見出しの最上位は h2 にしてあります。
- 見出しなし --- ブログタイトル
- h1 --- 記事タイトル
- h2以降 --- 記事内で利用される見出し
何故かというと、昔とはSEOの考え方が変化してきており、個別記事において最も重要な見出しはブログのタイトルではなく記事のタイトルである、という考え方に基づきます。
ここで問題になるのは、見出しレベルの違いによるTOCの不整合 です。簡単に言うと、TOC導入後は見出しレベルの変更不可 です。
例) 今までh3最上位で記事を書いてTOC導入 → h2最上位に変更したい場合
- 過去記事(h3最上位) --- TOC正常表示
- 新記事(h2最上位) --- TOC取得不可
- JSエラー
- 見た目の影響あり = 「目次」のタイトルだけが表示され、中身が無い状態
例) 今までh3最上位で記事を書いていた → h2最上位に変更してTOC導入する場合
- 過去記事(h3最上位) --- TOC取得不可
- JSエラー
- 見た目の影響なし = 何も表示されない(TOCを書き出すための要素が記されていないため)
- 新記事(h2最上位) --- TOC正常表示
TOC導入は確実に「見出しの方針」を決めてからにしてください。
FC2ブログでTOCを利用するには「追記」に記載
これは 絶対条件 と思ってください。これまで何度か 全文タイプテンプレートの難点 について記事を書いています。TOCスクリプトを利用するにも全文の一定条件下ではちょっと都合が悪いんですね。
TOCは記事内の見出しを抽出してリスト化するとともに、各見出しへの ページ内移動 も目的としています。TOCを本文に書いてしまうと結果的に全文を「本文」に書かざるを得なくなります。TOCが「本文」に、見出しが「追記」に、と別れているとトップページで表示されるTOCの内容と個別記事のTOCの内容が変わってしまいます。
えと。この理屈わかりますよね (´・ェ・`)
テンプレートの仕様にもよりますが、通常の全文タイプは「追記」をトップページに表示しません。あるいは敢えて「追記」をトップページにも表示してある場合のほとんどは初期設定 display: none でのクリック展開型になっていると思います。前者の「追記」が無い場合には「追記」内の見出しは抽出されず、後者の展開型は見出しが展開要素の中にあるとTOCのページ内移動が行き場を失ってしまいます。従って全部を「本文」に書かざるを得なくなる、という理屈です。
つまり TOCはトップページでは表示しない のが原則。トップに表示しないためには「追記」に記す、と。そゆこと。全文タイプテンプレートで「追記」を利用しない方や「追記」を展開型にしている方は実はSEO面でもいろいろと不利なんですYO (((ง'ω')و三 ง'ω')ڡ≡
加えてこれまで何度も記事にしているように、各記事の全内容がトップで表示されるとユーザビリティを損ないますのでおすすめできない。従って以下の内容に当てはまる方はご利用頂けません。
- 全文タイプテンプレートを利用しており、「追記」を利用していない
- 全文タイプテンプレートを利用しており「追記」も使っているが「本文」でも見出しを使っている
要約タイプテンプレートをご利用の方はTOCを「本文」に記載してもセーフではありますが、全文タイプに変更した時に泣くことになるか諦めることになりますのでやはり「追記」への掲載を絶対条件にした方が良いですね。
見出しは「追記」で利用する
TOCを「追記」に記載するのですから当然見出しは全て「追記」の中でなければいけません。仮にTOCよりも前に見出しが存在した場合には Cannot read property 'appendChild' of null というエラーでリストは生成できませんので注意(要素を追加する際に目印になる要素が先行取得出来ない時にこのエラーになります)
つまり TOCよりも前に見出しを使ってはいけない ということです。
場合によってはTOC不要で見出しを使いたい場合があるかと思います。その場合には「この見出しとこの見出しはTOCに」「この見出しはTOCに含めない」ではなく、ページ単位(記事単位)で「TOCを使うか使わないか」の両極の選択を行うようにしてください。TOCが要らない場合にはTOC書き出し用のhtmlを記載しない、TOCが要る時はhtmlを各見出しよりも先に書く。それだけです。
導入手順
ここからは実際の導入手順です。必須Javascriptは2つあります。
【TOCスクリプト】--- 必須script①
!function(e){"use strict";var t=function(e,t){for(var n in t)t.hasOwnProperty(n)&&t[n]&&(e[n]=t[n]);return e},n=function(e,t){var n=[],r=document.querySelectorAll(t);return Array.prototype.forEach.call(r,function(t){var r=t.querySelectorAll(e);n=n.concat(Array.prototype.slice.call(r))}),n},r=function(e){if("string"!=typeof e)return 0;var t=e.match(/\d/g);return t?Math.min.apply(null,t):1},o=function(e,t){for(;t--;)e=e.appendChild(document.createElement("ol")),t&&(e=e.appendChild(document.createElement("li")));return e},c=function(e,t){for(;t--;)e=e.parentElement;return e},i=function(e,t){return function(n,r,o){var c=n.textContent,i=t+"-"+o;r.textContent=c;var a=e?i:n.id||i;a=encodeURIComponent(a),n.id=a,r.href="#"+a}},a=function(e){var t=e.selector,a=e.scope,u=document.createElement("ol"),l=u,f=null,d=i(e.overwrite,e.prefix);return n(t,a).reduce(function(e,t,n){var i=r(t.tagName),a=i-e;a>0&&(l=o(f,a)),a<0&&(l=c(l,2*-a)),l=l||u;var p=document.createElement("li"),m=document.createElement("a");return d(t,m,n),l.appendChild(p).appendChild(m),f=p,i},r(t)),u},u=function(e){var n={selector:"h1, h2, h3, h4, h5, h6",scope:"body",overwrite:!1,prefix:"chapter"};e=t(n,e);var r=e.selector;if("string"!=typeof r)throw new TypeError("selector must be a string");if(!r.match(/^(?:h[1-6],?\s*)+$/g))throw new TypeError("selector must contains only h1-6");var o=location.hash;return o&&setTimeout(function(){var e=document.getElementById(o.slice(1));e&&e.scrollIntoView()},0),a(e)};"function"==typeof define&&define.amd?define(function(){return u}):e.initTOC=u}(window);
TOC本体、リストを生成するためのJSです。async指定を推奨しますので、外部ファイル化を行いたい方はこれ以降の内容も含める必要があるため後述します。テンプレートhtmlへ直貼りする方は以下のような形で記載してください。
圧縮済なのでソース内に改行を含めないようにしてください。
自身の可読性のために適当に改行を入れてしまう方が多いのですが、改行にはルールがあります。詳細な説明は省きますがとりあえず今回は行わないようにしてください。
<!--permanent_area--> <script>ここに上記内容をペースト</script> <!--/permanent_area-->
テンプレート記載位置は </body> の直前でOKです。body開始タグではなくbody終了タグの方ですのでお間違いなく。
【デフォルト設定上書き用スクリプト】--- 必須script②
var container = document.querySelector('#toc');
var toc = initTOC({
selector: 'h2, h3, h4, h5, h6',
scope: '#inner-contents',
overwrite: true,
prefix: 'chapter'
});
if(container) {
container.appendChild(toc);(window);
}
id名やclass名など不確定要素については上書き(overwrite)できるようにしてあります。つまり 上書き内容 = 確認必須内容 です。各個人によって指定すべき値が異なります。
selector指定はテンプレートの仕様と自身の方針を熟考してから決めてください。後から変更はできません(全記事の見出しレベルを手書きで修正できるなら別です)
selecor の指定
記事内で利用する最上位の見出しから最下位の見出しを指定してください。最上位はテンプレート「記事部分」内で利用可能な見出しを要確認。
scope の指定
追記を包含する要素のid名またはclass名を確認して指定。<%topentry_more> で検索し、それを挟む要素のidまたはclass属性の値を記す。
例1) id属性
<!--more-->
<div id="postscript">
<%topentry_more>
</div>
<!--/more-->
scope: '#postscript',
例2) class属性
<!--more-->
<div class="contents">
<%topentry_more>
</div>
<!--/more-->
scope: '.contents',
例3) id, classいずれの属性も無い場合
<!--more--> <%topentry_more> <!--/more-->
この場合にはdiv要素を追加(囲う)するか、<%topentry_body> と <%topentry_more> の 双方を 包含している要素についているidまたはclass名を指定。
例2-1) div要素の追加( + クラス属性を付与)
<!--more-->
<div class="tsuiki">
<%topentry_more>
</div>
<!--/more-->
scope: '.tsuiki',
例2-2) 本文, 追記双方を包含する要素のidまたはclass名
<div id="inner-contents">
<%topentry_body>
<!--more-->
<%topentry_more>
<!--/more-->
</div>
scope: '#inner-contents',
prefix の指定
各見出しにはid名が付与されます。そのid名をデフォルトでは chapter-数字 に設定していますが、chapter-数字 が既存の名称であった場合には変更してください(末尾の「ハイフン + 数字」は共通、変更不可です)
TOC本体スクリプトをテンプレートに直書きした方はその直下に記載
TOC本体スクリプトを外部化したい方はこの章の内容も含めてファイル化してください。できれば上書き用コードも圧縮します。

Minify JS and CSS online, or include the minifier in your project for on-the-fly compression.
Minify JS and CSS online, or include the minifier in your project for on-the-fly compression.
TOC本体JS内容 上書き用JS内容
本体コードの末尾で一度改行OKです。そして .js 拡張子で保存 → FC2サーバーへアップロード。
<script src="ここにJSファイルアドレス" async></script>
テンプレート記載位置は </body> 直前です。
外部化した場合にはテンプレート変更時に上書き内容を再度確認し、相違があれば新たにファイルを作成しなければいけません。単純に移設してしまわないよう注意。ファイル新規作成がめんどうな場合でそれでも本体を外部ファイル化しておきたい場合には
<script src="ここにTOC本体ファイルアドレス"></script> <script> ここに上書き用JSをインラインで記載 </script>
「インラインで記載」というのは外部ファイル化せずにJSコードを直書きすることです。本体は外部ファイル、上書き内容はインライン、という形。ただし async(非同期読み込み)の指定はできません のでページの表示スピードに多少影響が出ます。
【TOC書き出し用html】
<nav id="toc"></nav>
記事を書く際に「追記」に記載。特に変更などが生じない内容なので「もくじ」などで辞書登録しておくと簡単に呼び出せて便利です。このhtmlを記載すればTOCが表示され、記載しなければTOCは表示されません。
【「見出しリストに戻る」用html】
自動出力すると「TOCを表示したくない + 見出しを使いたい」という条件下でも戻るリンクだけが表示されてしまいますのでTOC本体スクリプトからは除外してあります。使いたい箇所に以下の内容を記載してください。「もどる」などで辞書登録しておくと良いでしょう。
<div class="back-toc" style="font-weight: bold;"><a href="#toc">目次へ戻る↑</a></div>
【デフォルトCSS】

/* toc */
#toc {
position: relative;
width: 100%;
margin: 30px auto;/* 30pxは先行要素と後続要素との距離 */
padding: 3em 40px 0 0;
border: 1px solid rgb(209,215,215);/* 全体を囲うボーダー */
background: rgb(249,255,255);/* 全体背景色 */
color: rgb(51,51,51);/* リストマーカー色 */
line-height: 1.5;/* 行間設定(注意 単位なしで記載) */
}
#toc::before {
content: "目次";/* toc上部タイトル */
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 3em;
font-weight: bold;
}
#toc a {
display: block;
width: 100%;
padding: 5px 0;
border-top: 1px solid rgb(229,235,235);/* 各リンク上部のボーダー */
color: rgb(51,51,51);/* リンク色 */
word-break: break-all;
text-decoration: none;
}
/* 戻るリンクの指定 */
.back-toc {
margin: 1em 0;
}
テンプレートスタイルシートの末尾に記載。
その他のCSSスタイリング
デフォルトのCSS内容と差し替えてください。同時記載ではなく差し替えです。
四角

四角吹き出し

リンクに背景色

リンクにドット背景

CSSデザインはそれこそ山程あります。数字に装飾を加える場合には counter-increment プロパティを利用するのが手っ取り早い方法なので、その雛形としてお使いください。
まとめ
スムーススクロールについてですが、弊ブログ提供のテンプレートの場合は何もしなくともスムーススクロール状態になります。他制作者様作品の場合はスムーススクロール適用されるかどうかはJS内容によります(おそらく自動では反映されません)ので、適用したい場合はTOC側ではなくスクロール側JSの変更が必要です。内容はまちまちなので私から「ここをこうすればOK」とは言えません。
TOCがあると固い印象を与えてしまいがちですがデザイン次第で柔らかくなります。GoogleはTOCを積極的に拾おうとしていますのでチャレンジしてみてください。
おまけ --- 2021年8月27日 追記
TOCの管理について。テンプレート変更などで移植作業が生じた際に、書き漏らしがあるようなので、以下のような記載方法をおすすめします。
<!-- TOC ここから, 移植必須 -->
内容
<!-- TOC ここまで, 移植必須 -->
内容に関しては、全て直書きの場合 及び 外部ファイルと上書きを別にする場合 は script要素が2つ(本体 + 上書き) で、全て外部ファイル化してまとめる場合は script要素が1つ です。
緑部位は コメントアウト と言い、管理用の目印として利用できます。<-- と --> に挟まれたものが コメント で、コメントは自身がわかりやすい文言にしておけばOKです。
ただし 記号部分を絶対に変更しないこと、コメント内容にハイフンを利用しないこと が条件です。
例) コメント内ハイフン
<!---------------------- コメント ---------------------->
<!-- コメント ---------------------- コメント -->
例) 記号の変更
<!!--コメント--!!>
<///コメント///>
あとはhtmlに直接コメントを書くのもダメです。必ず <-- と --> で囲います。
コメントアウトは本記事のTOCに関わらずhtml全般で利用できます。テンプレートhtmlに何かを任意で追加する際など、目印に付けておくと管理が楽になります。ただしくれぐれもルールに則って記載を行ってくださいね。
- ブロクカードのブックマークレットを修正しました2022/11/20
- 【Clipy】HTMLモードで記事を書くときの強力な助っ人アプリ2021/07/10
- ブログカード作成ブックマークレットをアップデートしました2021/07/09
- Google Chromeのおすすめ拡張機能3選2019/08/20
- ブログ作成に役立つブックマークレット2018/10/01
- ブログ作業効率を上げるためのPC小技いろいろ2018/09/19
- CSSで使える「カラーネーム」140色2018/07/29
- アイコンは【Font Awesome】だけじゃない 超便利【Icongram】2018/02/25
1.
1.1
1.1.1
みたいに表示させたいです…