2018年11月23日金曜日

Bloggerの記事に目次を自動作成する

Blogger使用開始2日目。

WordPressであれば、「Table of Contents Plus」というプラグインを利用して目次を作るところ、Bloggerにはないので、それに変わるものを検索してみました。

「Blogger 目次」でググった所、いろいろ見つかったので、コピペであっさりできるだろうと思いきや、結構苦労したので、メモを残します。

目次を作成する方法

ネットで調べた所、以下の3通りが見つかりました。
  1. HTMLで目次を作って、自分でリンクも貼る
  2. JavaScriptを組み込んで生成する。
  3. プラグインを組み込んで生成する。
一番確実なのは、1ですが(笑)、流石にそれはこの先面倒のため、残る選択肢は2か3となります。プラグインの組み込みも良いですが、プラグインが動かなかった場合のデバックが難しそうなため、2を選択しました。

JavaScriptで目次を自動作成する方法


Bloggerのテーマを選択してHTMLの編集を選択すると


HTMLレベルでソースが修正できるのでこちらでJavaScriptを修正します。


最初はコピペすれば良いと思ったので、下記の方のサイトを参考にさせていただきソースを修正しました。

人気のあるBloggerブログの作り方
<script type='text/javascript'>
  //<![CDATA[
  if (typeof(jQuery) == 'undefined') {
    document.write("<scr" + "ipt type=\"text/javascript\" src=\"//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js\"></scr" + "ipt>");
  }
  //]]>
</script>
<script type='text/javascript'>
  //<![CDATA[
  $(document).ready(function() {
    console.log("call func");
    $('a[name="more"]').after("<div id='toc' />");
    var idcount = 1;
    var h2cnt = 0;
    var h3cnt = 0;
    var h4cnt = 0;
    var toc = '';
    var currentlevel = 0;
    $(".post-body h2,.post-body h3,.post-body h4", this).each(function() {
      var chapid = "chapter-" + idcount;
      $(this).before("<div class='chapter-no' id='" + chapid + "' />");
      idcount++;
      var level = 0;
      var chapNo;
      if (this.nodeName.toLowerCase() == "h2") {
        level = 1;
        h2cnt++;
        h3cnt = 0;
        h4cnt = 0;
        chapNo = h2cnt + ".";
      } else if (this.nodeName.toLowerCase() == "h3") {
        level = 2;
        h3cnt++;
        h4cnt = 0;
        chapNo = h2cnt + "-" + h3cnt + ".";
      } else if (this.nodeName.toLowerCase() == "h4") {
        level = 3;
        h4cnt++;
        chapNo = h2cnt + "-" + h3cnt + "-" + h4cnt + ".";
      }
      if (currentlevel == level) {
        toc += "</li><li>";
      }
      while (currentlevel < level) {
        toc += '<ul class="chapter"><li>';
        currentlevel++;
      }
      while (currentlevel > level) {
        toc += "</li></ul><li>";
        currentlevel--;
      }
      toc += '<a href="#' + chapid + '">' + chapNo + $(this).text() + "</a>";
      $(this).html(chapNo + $(this).html());
    });
    while (currentlevel > 0) {
      toc += "</li></ul>";
      currentlevel--;
    }
    if ($(".post-body h2")[0]) {
      $("#toc").html(toc);
    } else {
      $('#toc').attr('class', 'no-toc');
    }
  });
  //]]>
</script>
<style><!--
  /*  目次のデザイン  */
  #toc:before{
    content:"目次";/*目次のタイトル*/
    padding-left:1em;
    font-weight:800;
  }
  #toc{
    background-color:#f9f9f9; /*目次の背景色*/
    padding:1em;
    display:block;
    margin:1em 0;
    border:1px solid #e6e6fa;/*目次の枠線*/
  }
  #toc li{list-style:none;margin-bottom:1em;}
  #toc ul{margin-bottom:0;}
  #toc:before{display:block;text-align:center;}
  .chapter-no{position: relative;top:-2.5em;}
--></style>
※</head>の直前に上記ソースを挿入します。
※目次出力場所には区切り線<!--more-->が必要です。

私の中では目次を自動生成して、デザインを少し自分好みにカスタマイズすれば終了のつもりだったので、30分程度で終わるかな~?とこの時は思っていました。
しかし、入れた結果がこれでした(笑)

目次のタイトルしかできていません。


自動生成してくれない原因は?

この時に真面目に貼り付けたソースを解析すれば良かったのですが、ずぼらな私はこちらの方法を諦めて、いろいろな方のサイトをのソースの切り貼りを頑張りました。しかしどれを真似しても動きません。
渋々原因を究明する事としました。


原因その1.Bliggerの新規投稿の投稿タイトルが小見出し(h3)だった



私の初投稿の記事はBloggerでいうと小見出し(h3)しか使っていないのですが、投稿タイトルは見出し(h2)を使用しているものとばかり思っていました。
しかし、実際に表示させてデバックすると、投稿タイトルは小見出し(h3)でした。
なので、初投稿の記事には見出し(h2)が存在していない事となります。

原因その2.目次生成タイミングが見出し(h2)だった

    参考にさせていただいたソースは下記記述となっていました。 
    if ($(".post-body h2")[0]) {
      $("#toc").html(toc);
    }else {
      $('#toc').attr('class', 'no-toc');
    }
 このh2に早くきづけば良かったのですが、ソースを斜め読みにしているため、なかなか気づきませんでした。これを見る限り、見出し(h2)があれば目次を作り、そうでなければ作らないようです。
なので、見出し(h2)のタイトルをつけるだけで出力されるようになりました。




原因を踏まえての対策

対策その1.見出し(h2)が無くても目次を出力するようにJavaScriptを修正


変更前
if ($(".post-body h2")[0]) {
      $("#toc").html(toc);
    }else {
      $('#toc').attr('class', 'no-toc');
    }
変更後
if ($(".post-body h2")[0]) {
      $("#toc").html(toc);
    } else if ($(".post-body h3")[0]) {
      $("#toc").html(toc);
    } else if ($(".post-body h4")[0]) {
      $("#toc").html(toc);
    }else {
      $('#toc').attr('class', 'no-toc');
    }

対策その2.見出し(h2)が無くても0カウンタの見出しを出力しないようにJavaScriptを修正

取り敢えず、対策その1.実行すれば目次は見出し(h2)が無くても作成されるのですが、参考にさせていただいたソースだとこんな、こんな目次ができちゃいます。
そう、見出し(h2)の分のカウンタが0として出てしまうんです。なので、追加で以下の対策です。
変更前
if (this.nodeName.toLowerCase() == "h2") {
        level = 1;
        h2cnt++;
        h3cnt = 0;
        h4cnt = 0;
        chapNo = h2cnt + ".";
      } else if (this.nodeName.toLowerCase() == "h3") {
        level = 2;
        h3cnt++;
        h4cnt = 0;
        chapNo = h2cnt + "-" + h3cnt + ".";
      } else if (this.nodeName.toLowerCase() == "h4") {
        level = 3;
        h4cnt++;
        chapNo = h2cnt + "-" + h3cnt + "-" + h4cnt + ".";
      }

変更後
 if (this.nodeName.toLowerCase() == "h2") {
        level = 1;
        h2cnt++;
        h3cnt = 0;
        h4cnt = 0;
        chapNo = h2cnt + ". ";
      }
      else if (this.nodeName.toLowerCase() == "h3") {
        level = 2;
        h3cnt++;
        h4cnt = 0;
        if(h2cnt==0){
   chapNo =h3cnt + ". ";
        }else{
   chapNo = h2cnt + "." + h3cnt + ". ";
        }
      }
      else if (this.nodeName.toLowerCase() == "h4") {
        level = 3;
        h4cnt++;
        if(h2cnt==0 && h3cnt==0 ){
   chapNo =h4cnt + " ";
        }else if (h2cnt==0 ){
   chapNo =  h3cnt + "." + h4cnt + ". ";
        }else{
         chapNo = h2cnt + "." + h3cnt + "." + h4cnt + ". ";
        }
      }

めっちゃベタにIf文で分岐しているだけなので、全然スマートでは無いのですが(^^;
もうちょっとWEB関連のソースに慣れてきたら、改善するかもしれません。
あと、目次も-(ハイフン)繋がりが見づらかったため.(ピリオド)繋がりに変更してあり、最後のピリオドの後には半角スペースを入れてあります。

完成版全ソース
<!--20181122 目次表示Script、style追加 S-->

<script type='text/javascript'>
  //<![CDATA[
  if (typeof(jQuery) == 'undefined') {
console.log('iso jQuery undefined');
    document.write("<scr" + "ipt type=\"text/javascript\" src=\"//ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></scr" + "ipt>");
  }
  //]]>
</script>
<script type='text/javascript'>
  //<![CDATA[
  $(document).ready(function() {
console.log("iso call func");
    $('a[name="more"]').after("<div id='toc' />");
    var idcount = 1;
    var h2cnt = 0;
    var h3cnt = 0;
    var h4cnt = 0;
    var toc = '';
    var currentlevel = 0;
    $(".post-body h2,.post-body h3,.post-body h4", this).each(function() {
      var chapid = "chapter-" + idcount;
      $(this).before("<div class='chapter-no' id='" + chapid + "' />");
      idcount++;
      var level = 0;
      var chapNo;
      if (this.nodeName.toLowerCase() == "h2") {
        level = 1;
        h2cnt++;
        h3cnt = 0;
        h4cnt = 0;
        chapNo = h2cnt + ". ";
      }
      else if (this.nodeName.toLowerCase() == "h3") {
        level = 2;
        h3cnt++;
        h4cnt = 0;
        if(h2cnt==0){
chapNo =h3cnt + ". ";
        }else{
chapNo = h2cnt + "." + h3cnt + ". ";
        }
     
      }
      else if (this.nodeName.toLowerCase() == "h4") {
        level = 3;
        h4cnt++;
        if(h2cnt==0 && h3cnt==0 ){
chapNo =h4cnt + " ";
        }else if (h2cnt==0 ){
chapNo =  h3cnt + "." + h4cnt + ". ";
        }else{
        chapNo = h2cnt + "." + h3cnt + "." + h4cnt + ". ";
        }
      }
      if (currentlevel == level) {
        toc += "</li><li>";
      }
      while (currentlevel < level) {
        toc += '<ul class="chapter"><li>';
        currentlevel++;
      }
      while (currentlevel > level) {
        toc += "</li></ul><li>";
        currentlevel--;
      }
      toc += '<a href="#' + chapid + '">' + chapNo + $(this).text() + "</a>";
      $(this).html(chapNo + $(this).html());
    });
    while (currentlevel > 0) {
      toc += "</li></ul>";
      currentlevel--;
    }
    if ($(".post-body h2")[0]) {
      $("#toc").html(toc);
    } else if ($(".post-body h3")[0]) {
      $("#toc").html(toc);
    } else if ($(".post-body h4")[0]) {
      $("#toc").html(toc);
    }else {
      $('#toc').attr('class', 'no-toc');
    }
  });
  //]]>
</script>
<style><!--
  /*  目次のデザイン  */
  #toc:before{
    content:"目次";/*目次のタイトル*/
    padding-left:1em;
    font-weight:800;
  }
  #toc{
    background-color:#f9f9f9; /*目次の背景色*/
    padding:1em;
    display:block;
    margin:1em 0;
    border:1px solid #e6e6fa;/*目次の枠線*/
  }
  #toc li{list-style:none;margin-bottom:1em;}
  #toc ul{margin-bottom:0;}
  #toc:before{display:block;text-align:center;}
  .chapter-no{position: relative;top:-2.5em;}
--></style>
<!--20181122 目次表示Script、style追加 E-->

※</head>の直前に上記ソースを挿入します。
※目次出力場所には区切り線<!--more-->が必要です。

その他の対策

投稿タイトルが小見出し(h3)であることが非常に気に入らないため、投稿タイトルを見出し(h2)にすることにしました。
この中でひたすら、下記記述をh3→h2に変更しました。

変更前
<h3 class='post-title entry-title'
変更後
<h2 class='post-title entry-title'


※WEB関連の経験が少ないので、ご意見アドバイスお待ちしております~!


2 件のコメント:

  1. こんばんは。
    わたしもbloggerデビューしたばかりで、
    同じく「目次」の作成に苦労しました。
    ていうかまだできてません…
    このブログみて試してみますね!
    ありがとうございます!

    返信削除
    返信
    1. マツユナイテッドさん、コメントありがとうございます。嬉しいです~(*^^*)

      私も今、右も左もわからない状態でスタートしています。
      もし、同じ方法でうまくいかなかったらご相談下さい。
      一緒に解決方法探しましょう!

      削除