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

2019/10/19 3

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

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

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

スケ郎のお話image[Blogger] 目次を簡単に自動生成(忙しい人向けのコピペ素材)https://www.sukerou.com/2018/10/blogger-table-of-contents-javascript.html
デザインはこれまでの人力目次と同じになるように調整しています。その他細かい所も自分好みにカスタマイズ済み。

↓が自動生成された目次です。

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

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



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

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

ふじろじっくカスタム

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

    <b:if cond='data:view.isSingleItem'>
HTML
<!-- [START] 目次作成プラグイン-->
<b:if cond='data:view.isSingleItem'>
  <script>
    //以下のオプションを好みに合わせて変更して下さい
    //オプションの詳しい説明は、(https://www.sukerou.com/2018/10/blogger-table-of-contents-javascript.html)を参照
    var toc_options = {
      target: [&quot;h2&quot;, &quot;h3&quot;, &quot;h4&quot;],
      autoNumber: true,
      condTargetCount: 3,
      insertPosition: &quot;firstHeadBefore&quot;,
      showToc: true,
      width: &quot;100%&quot;,
      marginTop: &quot;20px&quot;,
      marginBottom: &quot;20px&quot;,
      indent: &quot;20px&quot;,
      postBodySelector: &quot;.widget.Blog&quot;
    };
    //これ以降のソースは編集しないでください
    (function(i){var j=0;document.addEventListener(&quot;DOMContentLoaded&quot;,function(){var p=document.querySelector(toc_options.postBodySelector);if(p==null||typeof p===&quot;undefined&quot;){reutrn}if(toc_options.target.length==0){return}rootContent=h(toc_options,p);if(rootContent.children.length&gt;=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&lt;u-1?q.target[E+1]:&quot;&quot;;var y=(++j);var F=g(z,m(D),E+1,y);w.children.push(F);D.id=y;var A=f(D);if(x==&quot;&quot;){return}while(true){if(A==null||typeof A===&quot;undefined&quot;){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&lt;B.length;C++){t(E+1,B[C],F)}}var A=f(A)}};var r=g(&quot;ROOT&quot;,&quot;&quot;,0);var v=p.getElementsByTagName(q.target[0]);for(var s=0;s&lt;v.length;s++){t(0,v[s],r,&quot;&quot;)}return r}function c(s){var r=document.createElement(&quot;div&quot;);r.classList.add(&quot;b-toc-container&quot;);r.style.marginTop=toc_options.marginTop;r.style.marginBottom=toc_options.marginTop;if(toc_options.width==&quot;100%&quot;){r.style.display=&quot;block&quot;}else{r.style.width=toc_options.width}var q=document.createElement(&quot;p&quot;);var w=document.createElement(&quot;span&quot;);var v=document.createElement(&quot;span&quot;);var u=document.createElement(&quot;span&quot;);v.classList.add(&quot;b-toc-show-wrap&quot;);u.classList.add(&quot;b-toc-show-wrap&quot;);var y=document.createElement(&quot;a&quot;);w.innerText=&quot;目次&quot;;v.innerText=&quot; [&quot;;u.innerText=&quot;]&quot;;y.href=&quot;javascript:void(0);&quot;;q.appendChild(w);q.appendChild(v);q.appendChild(y);q.appendChild(u);var t=function(z){var p=typeof z===&quot;boolean&quot;?z:e(r,&quot;hide&quot;);if(p){y.innerText=&quot;非表示&quot;;r.classList.remove(&quot;hide&quot;)}else{y.innerText=&quot;表示&quot;;r.classList.add(&quot;hide&quot;)}};y.addEventListener(&quot;click&quot;,t);t(toc_options.showToc);var x=document.createElement(&quot;ul&quot;);s.children.forEach(function(z,p){n(x,z,(p+1)+&quot;&quot;)});r.appendChild(q);r.appendChild(x);return r}function n(s,u,w){var p=document.createElement(&quot;li&quot;);var q=document.createElement(&quot;a&quot;);p.style.paddingLeft=toc_options.indent;q.href=&quot;#&quot;+u.id;if(toc_options.autoNumber){var t=document.createElement(&quot;span&quot;);t.classList.add(&quot;toc-number&quot;);t.innerText=w}var v=document.createElement(&quot;span&quot;);v.classList.add(&quot;toc-text&quot;);v.innerText=u.text;if(toc_options.autoNumber){q.appendChild(t)}q.appendChild(v);p.appendChild(q);s.appendChild(p);if(u.children.length&gt;0){var r=document.createElement(&quot;ul&quot;);p.appendChild(r);u.children.forEach(function(y,x){n(r,y,w+&quot;-&quot;+(x+1))})}}function o(q){var r=null;var p=document.querySelector(toc_options.postBodySelector);if(toc_options.insertPosition==&quot;firstHeadBefore&quot;||toc_options.insertPosition==&quot;firstHeadAfter&quot;){r=p.querySelector(toc_options.target[0])}else{if(toc_options.insertPosition==&quot;top&quot;){r=p}}if(r==null){return}if(toc_options.insertPosition==&quot;firstHeadBefore&quot;){k(r,q)}else{if(toc_options.insertPosition==&quot;firstHeadAfter&quot;){a(r,q)}else{if(toc_options.insertPosition==&quot;top&quot;){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&amp;&amp;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 {
    background:#fff;
    border:1px solid #eee;
    border-radius:8px;
    display: table;
    padding:1.5em;
    margin:2em auto 3em!important;
    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&gt;ul {
    margin:15px 0 0;
}
.b-toc-container.hide&gt;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;
    color: #555;
}
.toc-number:after {
    content:&quot;\002E&quot;
}
.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-show-wrap {
    display: none;
}
.b-toc-container p a {
    float: right;
    margin-left: -3em;
    font-size: .7em;
    color: #111!important;
    line-height: 1.8em;
    text-decoration: none!important;
    padding: 0 5px;
    background: #ddd;
    border-radius: 2px;
}
.b-toc-container p a:hover {
    opacity: .8;
    color: #333!important;
}
  </style>
</b:if>
<!-- [END] 目次作成プラグイン-->

ヌルっと移動

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

目次をクリックすると滑らかに見出しに移動します。



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

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

TechAcademyマガジンjQueryでスムーススクロールを実装する方法【初心者向け】プログラミング初心者向けに、jQueryでスムーススクロールを実装する方法について解説しています。Webサイトでもよく見る「トップに戻る」ような機能を手軽に作ることができるので、ぜひ自分で書いてみましょう。https://techacademy.jp/magazine/9532
当ブログでは元々 jQuery は導入済みだったので、以下のスクリプトを追加するだけで実装できました。

return false; は目次をクリックした時に URL 末尾に #アンカータグを付かなくするためのコードです。
ウチでは // でコメントアウトして無効化してます。つまりアンカータグが付くようになってます。

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

Script
<!--Smooth Scroll-->
<b:if cond='data:view.isSingleItem'>
<script>
$(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 false;
  });
});
</script>
</b:if>

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

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

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

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

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

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


2020/01/14 追記

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

上記スクリプトに赤文字部分を追記するだけです。

HTML
<!--Smooth Scroll-->
<b:if cond='data:view.isSingleItem'>
<script>
$(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 false;
  });
});
</script>
</b:if>

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


2020/05/19 追記

昨日、目次の表示に関する修正がされましたので、当記事のコードもアップデートしておきました。(上記コードの青文字部分)

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

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



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



ARCHIVE

RECENT POSTS