【JavaScript】ブログ記事の目次を自動で作成してみる

JavaScript

【JavaScript】ブログ記事の目次を自動で作成してみる

さて、タイトル通り。
当ブログにも一度目次にアンカーリンクを付けたものを配置したが、なんといっても作るのが面倒なのである。
2箇所に細工をしなくてはならないのだ。とにかく面倒なことはしたくないので、記事の書き方をルール化した上で自動で生成されるようにしたい。
自動化できることはどんどんやったらいいさw

まずは構想から行ってみようかしら。

この下に表示されている目次はJavaScriptで記事を読み取り自動で生成したものです。
こんなものイチイチ手動で書いてられんで。


構想(妄想)

まず、対象ぺージをどれにするか
全ぺージはさすがに不要と思われるので却下。
ブログ記事のみとするが、実装をどうするか


記事内に目次用のタグを作成し、プログラムが目次用のタグを読み込めた場合にのみ自動実装処理を行う。


考えながら実装

作成位置

専用のDIV要素を配置することによって、その中に自動作成するようにする。
idはpageContent
この専用のDIV要素がない場合は作成処理を行わない。

<div id="pageContent"></div>
デザイン

デザインは使用しているテーマに沿ってクラスを割り当て。
アコーディオンの機能を使用する。
幅はデフォルト。つまり100%になる。
detailsタグにpanelクラスとaccordionクラスを使用しよう。

  let detailsElement = document.createElement("details");
  detailsElement.classList.add("panel");
  detailsElement.classList.add("accordion");
何をアンカーとするか

h1タグはタイトルで使用しているので、h2~h6を対象でいいかと。
できればインデントも付けよう。
インデントはこんな感じで判断できるかな。
マジ適当すぎる。


    let indent = "";
    switch (headerElements[headerCount].tagName) {
      case 'H2':
        indent = Array(2+1).join("&nbsp");
        break;
      case 'H3':
        indent = Array(4+1).join("&nbsp");
        break;
      case 'H4':
        indent = Array(6+1).join("&nbsp");
        break;
      case 'H5':
        indent = Array(8+1).join("&nbsp");
        break;
      case 'H6':
        indent = Array(10+1).join("&nbsp");
        break;
    }

できあがり

全コードはこんな感じ。
まぁ、やっつけで作った感がありますねwwww

h2~h6にhref属性を動的に追加しています。
また「人気タグ」と「フィード」が含まれてしまいますので、それらは除外しています。

  let pageContentElement = document.getElementById("pageContent");

  if ( pageContentElement === null ) {
    return;
  }

  const headerElements = document.querySelectorAll("h2, h3, h4, h5, h6")

  if ( headerElements.length === 0 ) {
    return;
  }
  if ( headerElements[headerCount].innerHTML === "人気タグ" ) {
    continue;
  }
  if ( headerElements[headerCount].innerHTML === "フィード" ) {
    continue;
  }

  let detailsElement = document.createElement("details");
  detailsElement.classList.add("panel");
  detailsElement.classList.add("accordion");
  detailsElement.classList.add("bg-secondary");
  detailsElement.open = true;

  let summaryElement = document.createElement("summary");
  summaryElement.classList.add("accordion-header");
  summaryElement.classList.add("bg-primary");
  let iElement = document.createElement("i")
  iElement.classList.add("icon");
  iElement.classList.add("icon-arrow-right");
  iElement.classList.add("mr-1");

  summaryElement.appendChild(iElement);
  summaryElement.insertAdjacentHTML("beforeend", "目次");

  detailsElement.appendChild(summaryElement);

  let accodionBodyElement = document.createElement("div");
  accodionBodyElement.classList.add("accordion-body");

  let ulElement = document.createElement("ul");
  ulElement.classList.add("menu");
  ulElement.classList.add("menu-nav");

  for ( let headerCount = 0; headerCount < headerElements.length; headerCount ++){

    if ( headerElements[headerCount].innerHTML === "" ) {
      continue;
    }

    let indent = "";
    switch (headerElements[headerCount].tagName) {
      case 'H2':
        indent = Array(2+1).join("&nbsp");
        break;
      case 'H3':
        indent = Array(4+1).join("&nbsp");
        break;
      case 'H4':
        indent = Array(6+1).join("&nbsp");
        break;
      case 'H5':
        indent = Array(8+1).join("&nbsp");
        break;
      case 'H6':
        indent = Array(10+1).join("&nbsp");
        break;
    }

    headerElements[headerCount].id = "pageContentId_" + headerCount;

    let liElement = document.createElement("li");
    liElement.classList.add("menu-item");
    let aElement = document.createElement("a");
    aElement.href = "#pageContentId_" + headerCount;;
    aElement.innerHTML = indent + "・" + headerElements[headerCount].innerHTML;
    liElement.appendChild(aElement);
    ulElement.appendChild(liElement);
  }

  accodionBodyElement.appendChild(ulElement);
  detailsElement.appendChild(accodionBodyElement);

  pageContentElement.appendChild(detailsElement);

閉じた状態

開いた状態

細かいデザインは後で調整しよう。


これで面倒なこととはおさらば。
ただブログを書くだけ、とは言え面倒なことは面倒。
多少システマチックにすればだいぶ楽になる。

ま、SEOは全く考慮していませんがwwww

いやー…ただね。
やはりフロントの人間じゃないから書き方の汚いこと。

前の記事 次の記事