EDIT

自動目次とスムーススクロール

2023/09/065
アイキャッチ

目次を自動生成してくれるスクリプトを導入しました。

実は今まで当ブログの一部の記事・ページに付けていた目次は完全に手入力の人力作成💧だったんですが、今後は何もしなくても見出しの含まれる記事では勝手に表示してくれるようになるので、記事作成も楽になります。

こちらのスケ郎さんが作られたスクリプトを手順通りに設置しただけで簡単に導入できました。

ファビコンスケ郎のお話
サムネイル
[Blogger] 目次を簡単に自動生成(忙しい人向けのコピペ素材)
https://www.sukerou.com/2018/10/blogger-table-of-contents-javascript.html

デザインはこれまでの人力目次と同じになるように調整しています。その他細かい所も自分好みにカスタマイズ済み。

↓が自動生成された目次です。
IB-Note 作の自動生成目次に乗り換えました。

表示・非表示ボタンいる?

ところで前から疑問に思っていたのですが、この手の目次には必ず「表示・非表示(隠す)」ボタンというものが付いていますけど、これっていりますかね?

目次プラグイン

デフォルトで非表示状態にしているのであれば、当然「表示(開く)」ボタンは必要になりますけど、初めから開いてる場合、わざわざ閉じる人なんています? 中には目障りに感じる人とかいるんだろうか・・・(~_~;)

私はそんな人はいないであろうという判断で、開閉ボタンは消しました。無くても何ら支障はないはずだと思いますけどどうでしょう。

ふじろじっくカスタム

ちなみにうちでカスタマイズした内容は…

  • 開閉ボタンは display:none で消しました。
  • 「これ以降のソースは編集しないでください」という部分もちょっとだけいじってます(^^;
  • 記事だけでなく固定ページにも表示させたいので、条件分岐タグを以下のように変更しました。
  • <b:if cond='data:blog.pageType == "item"'>

    <b:if cond='data:view.isSingleItem'>

<!-- [START] 目次作成プラグイン-->
<b:if cond='data:view.isSingleItem'>
  <script>
//<![CDATA[
    //以下のオプションを好みに合わせて変更して下さい
    //オプションの詳しい説明は、(https://www.sukerou.com/2018/10/blogger-table-of-contents-javascript.html)を参照
    var toc_options = {
      target: ["h2", "h3", "h4"],
      autoNumber: true,
      condTargetCount: 3,
      insertPosition: "firstHeadBefore",
      showToc: true,
      width: "100%",
      marginTop: "2em",
      marginBottom: "3em",
      indent: "0",
      postBodySelector: ".widget.Blog"
    };
    //これ以降のソースは編集しないでください
    (function(i){var j=0;document.addEventListener("DOMContentLoaded",function(){var p=document.querySelector(toc_options.postBodySelector);if(p==null||typeof p==="undefined"){reutrn}if(toc_options.target.length==0){return}rootContent=h(toc_options,p);if(rootContent.children.length>=toc_options.condTargetCount){var q=c(rootContent);o(q)}});function h(q,p){var u=q.target.length;var t=function(E,D,w){var z=q.target[E];var x=E<u-1?q.target[E+1]:"";var y="toc_"+(++j);var F=g(z,m(D),E+1,y);w.children.push(F);D.id=y;var A=f(D);if(x==""){return}while(true){if(A==null||typeof A==="undefined"){break}if(b(A)==z){break}if(b(A)==x){t(E+1,A,F)}else{var B=A.getElementsByTagName(x);for(var C=0;C<B.length;C++){t(E+1,B[C],F)}}var A=f(A)}};var r=g("ROOT","",0);var v=p.getElementsByTagName(q.target[0]);for(var s=0;s<v.length;s++){t(0,v[s],r,"")}return r}function c(s){var r=document.createElement("div");r.classList.add("b-toc-container");r.style.marginTop=toc_options.marginTop;r.style.marginBottom=toc_options.marginBottom;if(toc_options.width=="100%"){r.style.display="block"}else{r.style.width=toc_options.width}var q=document.createElement("p");var w=document.createElement("span");var v=document.createElement("span");var u=document.createElement("span");v.classList.add("b-toc-show-wrap");u.classList.add("b-toc-show-wrap");var y=document.createElement("a");w.innerText="目次";v.innerText=" [";u.innerText="]";y.href="javascript:void(0);";q.appendChild(w);q.appendChild(v);q.appendChild(y);q.appendChild(u);var t=function(z){var p=typeof z==="boolean"?z:e(r,"hide");if(p){y.innerText="非表示";r.classList.remove("hide")}else{y.innerText="表示";r.classList.add("hide")}};y.addEventListener("click",t);t(toc_options.showToc);var x=document.createElement("ul");s.children.forEach(function(z,p){n(x,z,(p+1)+"")});r.appendChild(q);r.appendChild(x);return r}function n(s,u,w){var p=document.createElement("li");var q=document.createElement("a");p.style.paddingLeft=toc_options.indent;q.href="#"+u.id;if(toc_options.autoNumber){var t=document.createElement("span");t.classList.add("toc-number");t.innerText=w}var v=document.createElement("span");v.classList.add("toc-text");v.innerText=u.text;if(toc_options.autoNumber){q.appendChild(t)}q.appendChild(v);p.appendChild(q);s.appendChild(p);if(u.children.length>0){var r=document.createElement("ul");p.appendChild(r);u.children.forEach(function(y,x){n(r,y,w+"-"+(x+1))})}}function o(q){var r=null;var p=document.querySelector(toc_options.postBodySelector);if(toc_options.insertPosition=="firstHeadBefore"||toc_options.insertPosition=="firstHeadAfter"){r=p.querySelector(toc_options.target[0])}else{if(toc_options.insertPosition=="top"){r=p}}if(r==null){return}if(toc_options.insertPosition=="firstHeadBefore"){k(r,q)}else{if(toc_options.insertPosition=="firstHeadAfter"){a(r,q)}else{if(toc_options.insertPosition=="top"){k(r,q)}}}}function g(q,r,p,s){return{tagName:q,text:r,children:[],nestLevel:p,id:s}}function m(p){return p.innerText}function f(p){return p.nextElementSibling}function d(p){return p.previousElementSibling}function b(p){return p.tagName.toLowerCase()}function e(p,q){return p.classList.contains(q)}function l(p){return p.parentNode}function a(q,s){var r=l(q);var p=f(q);if(r!=null&&p!=null){r.insertBefore(s,p)}}function k(p,r){var q=l(p);if(q!=null){q.insertBefore(r,p)}}})(window);
  //]]>
</script>
  <style type="text/css">
.b-toc-container {
    position: relative;
    background:#fff;
    border:1px solid #eee;
    border-radius:8px;
    display: table;
    padding:1.5em;
    margin-right: auto;
    margin-left: auto;
    box-shadow:10px 10px 15px -10px #aaa;
    width:80%;
}
.b-toc-container p {
    text-align:center;
    margin:0;
    padding:0;
}
.b-toc-container p span:nth-of-type(1) {
    font-weight:bold;
    font-size:1.3em;
    font-family:arial;
    color:#555;
}
.b-toc-container ul {
    list-style-type:none;
    list-style:none;
    margin:0;
    padding:0;
}
.b-toc-container > ul {
    margin:15px 0 0;
}
.b-toc-container.hide > ul {
    display:none;
}
.b-toc-container ul li {
    margin:0;
    padding:0 0 0 10px;
    list-style:none;
    line-height:1.7em;
}
.toc-number {
    font-weight:bold;
    padding-left: 5px;
}
.toc-number:after {
    content:"\002E";
}
.b-toc-container ul li a {
    color:#42a5f5;
    display: flex; /*折返し調整の追記*/
    align-items: flex-start; /*折返し調整の追記*/
    flex-wrap: nowrap; /*折返し調整の追記*/
}
.b-toc-container ul li .toc-number {
    margin:0 5px 0 0;
    white-space: nowrap; /*折返し調整の追記*/
}
.b-toc-container ul li a:hover {
    color: #fff!important;
    text-decoration: none!important;
    background: #6495ed;
}
/*表示・非表示ボタン*/
.b-toc-container p a {
    display:;/*ボタンを非表示にする場合は none*/
    position: absolute;
    right: 10px;
    top: 10px;
    font-size: .7em;
    color: #111!important;
    line-height: 1.8em;
    text-decoration: none!important;
    padding: 0 5px;
    background: #ddd;
    border-radius: 2px;
    opacity: .8;
}
.b-toc-show-wrap {
    display: none;
}
.b-toc-container p a:hover {
    opacity: 1;
    color: #333!important;
}
  </style>
</b:if>
<!-- [END] 目次作成プラグイン-->

ヌルっと移動

さて、目次は満足いく形で設置できましたけど、さらにもっと見栄え良くなるようなカスタマイズを施したいなというわけで、スムーススクロールというものも実装してみました。

瞬間移動よりこっちの方がいいでしょ?

スムーススクロールのスクリプトはこちらの記事を参照させていただきました。

ファビコンTechAcademyマガジン - 教育×テクノロジーのWebメディア
サムネイル
jQueryでスムーススクロールを実装する方法 | TechAcademyマガジン
プログラミング初心者向けに、jQueryでスムーススクロールを実装する方法について解説しています。Webサイトでもよく見る「トップに戻る」ような機能を手軽に作ることができるので、ぜひ自分で書いてみましょう。
https://techacademy.jp/magazine/9532

当ブログでは元々 jQuery は導入済みだったので、以下のスクリプトを追加するだけで実装できました。(jQuery より下に置かないと動作しない)

12行目のreturn false; は目次をクリックした時に URL 末尾に #アンカータグを付かなくするためのコードです。
うちでは true に変えています。つまりアンカータグが付くようになってます。

トップページでは必要ないのと、番号ページネーションのスクリプトと干渉するのを防ぐために記事・固定ページでのみ有効にする条件分岐タグも付けています。

<!--Smooth Scroll-->
<b:if cond='data:view.isSingleItem'>
<script>
//<![CDATA[
$(function(){
  $('a[href^="#"]').click(function(){
    var speed = 1000;
    var href= $(this).attr("href");
    var target = $(href == "#" || href == "" ? 'html' : href);
    var position = target.offset().top;
    $("html, body").animate({scrollTop:position}, speed, "swing");
    return true;
  });
});
//]]>
</script>
</b:if>

目次を完全に表示させたくない場合

一般的にはそういうケースはあまりないのかもしれませんが、ウチでは見出しが複数あっても目次を表示させたくない記事やページがあります。(目次用途で見出しを付けてない等の理由)

目次を表示する見出しの数を、例えば「3」に指定すれば見出しが3つ未満の場合目次が表示されませんけど、指定した見出し数に関わらず目次を表示させたくない場合は、記事編集のHTMLモードで以下のスタイルコードを追記することで出したくない記事で非表示にしています。

<style>.b-toc-container{display:none!important}</style>

ちなみにスケ郎さん、目次を出す/出さないを選べる機能の実装も検討されているようです。→ 作者さんのコメント

初めからこういう選択機能があれば便利だと思うので、是非実装して欲しいです。

2020/01/14 追記: ナビバーと見出しが被る問題の改善方法

当ブログのようにナビゲーションバー(ヘッダーメニュー)が position:fixed 等で固定されている場合、スクロール停止位置の見出しにメニューが被ってしまうという問題がありますが、この問題を解決できる方法がありました。

上記スクリプトを以下のようにすると、ちょうどいい位置で止まってくれるようになります。

<!--Smooth Scroll-->
<b:if cond='data:view.isSingleItem'>
<script>
//<![CDATA[
$(function(){
    var headerHight = 30; //固定ヘッダーメニューの高さ(px)
  $('a[href^="#"]').click(function(){
    var speed = 1000;
    var href= $(this).attr("href");
    var target = $(href == "#" || href == "" ? 'html' : href);
    var position = target.offset().top-headerHight;
    $("html, body").animate({scrollTop:position}, speed, "swing");
    return true;
  });
});
//]]>
</script>
</b:if>

【参考記事】

ファビコンTips Note by TAM
サムネイル
position:fixedでヘッダ固定時のページ内リンクのずれを解消したい | Tips Note by TAM
TAM のテクニカルチームがお届けする WEB技術ブログ!
http://www.tam-tam.co.jp/tipsnote/html_css/post4776.html

2020/05/19 追記: アップデート

昨日、目次の表示に関する修正がされましたので、当記事のコードもアップデートしておきました。(CSS の/*折返し調整の追記*/の4行)

[Blogger] 目次を簡単に自動生成(忙しい人向けのコピペ素材)(2020/05/18 追記)

長い見出しで文字列が折り返される場合に、目次番号の幅の分だけ字下げするように改善されました。

目次プラグイン インデント調整版

だいぶ見やすくなりましたね。

2022/04/24 追記

こちらの IB-Note さん作の自動生成目次スクリプトに変更しました。

スケ郎さんのものよりコードがシンプルで読み込みも早くておすすめです!

ファビコンIB-Note
サムネイル
【全見出しタグ対応】高速&シンプルな自動生成目次 | IB-Note
いろいろな自動生成目次のソースコードを読んで比較検討を重ねた結果、「これだ!」と思う理想の目次を作ることができました。
https://itblogger-note.blogspot.com/2021/12/fast-and-simple-auto-toc.html