Written by Kasumi

JavaScriptを使い、目次をプラグイン無しで自動生成する方法

JavaScriptを使い、WordPress記事などの目次をプラグイン無しで実装したい。

目次に順番がわかるように番号を付与したい。

本記事ではこのような悩みを解決。

実装方法について解説します。

目次の実装

// 目次生成
document.addEventListener('DOMContentLoaded', () => {
  var heads = document.querySelectorAll('h2, h3, h4, h5, h6');
  var className = 'head'; //クラス
  var numText = 0; //目次番号

  // 目次番号 初期値
  var num02 = 0;
  var num03 = 0;
  var num04 = 0;
  var num05 = 0;
  var num06 = 0;

  // hタグの数だけループ
  // 上位のhタグに上がると、下位のhタグ目次番号をリセット
  if (heads && heads.length) {
    var contents = '';
    heads.forEach((head, i) => {
      if (head.localName == 'h2') {
        className = 'toc02';
        num02++;
        numText = num02;
        num03 = 0;
      } else if (head.localName == 'h3') {
        className = 'toc03';
        num03++;
        numText = num02 + '.' + num03;
        num04 = 0;
      } else if (head.localName == 'h4') {
        className = 'toc04';
        num04++;
        numText = num02 + '.' + num03 + '.' + num04;
        num05 = 0;
      } else if (head.localName == 'h5') {
        className = 'toc05';
        num05++;
        numText = num02 + '.' + num03 + '.' + num04 + '.' + num05;
        num06 = 0;
      } else if (head.localName == 'h6') {
        className = 'toc06';
        num06++;
        numText = num02 + '.' + num03 + '.' + num04 + '.' + num05 + '.' + num06;
      }

      // hタグにアンカーを付与
      head.innerHTML += `<span id="head${i}"></span>`;
      // 目次にhタグのタイトルリストを追加
      contents += `<li class="${className}"><a href="#head${i}"><span>${numText}</span>${head.textContent}</a></li>`;
    })
    document.querySelector('#toc').innerHTML += `<ul>${contents}</ul>`;
  }
});

目次を自動生成するJavaScriptコード例です。

予め、ID要素tocをdivタグで生成してください。

その中に目次が生成されます。

実装方法の詳細は以下の通りです。

DOMを読み込んでから実行する関数を定義

まず最初にaddEventListener(‘DOMContentLoaded’)を定義。DOMを読み込んでからJavaScriptが実行するようにします。

hタグを全て取得。目次番号の初期値を定義

querySelectorAllをつかって記事内に入ってるhタグ(本記事ではh2からh6まで)を全て取得します。

次に本記事では目次のリストに数値で順番を振りたいので、目次番号を変数として定義。

初期値で0を代入します。

forEachでhタグの数だけループ。目次リストを生成

forEachを使ってquerySelectorAllで取得したhタグの数だけアンカーリンクが入ったタイトルリストを目次内に生成します。

if文でhタグ毎に条件分岐。

同じhタグがループされる度にページ番号が加算。

上位のhタグになったら下位のページ番号がリセットされるようにします。

hタグにアンカーを設定

hタグにinnerHTMLを使ってアンカーを設定。

目次のタイトルをクリックしたら該当するhタグへページ内リンクされるようにします。

以上で目次の実装完了です。

目次をクリックしたらスムーススクロールさせる

// 目次生成
document.addEventListener('DOMContentLoaded', () => {
  var heads = document.querySelectorAll('h2, h3, h4, h5, h6');
  var className = 'head'; //クラス
  var numText = 0; //目次番号

  // 目次番号 初期値
  var num02 = 0;
  var num03 = 0;
  var num04 = 0;
  var num05 = 0;
  var num06 = 0;

  // hタグの数だけループ
  // 上位のhタグに上がると、下位のhタグ目次番号をリセット
  if (heads && heads.length) {
    var contents = '';
    heads.forEach((head, i) => {
      if (head.localName == 'h2') {
        className = 'toc02';
        num02++;
        numText = num02;
        num03 = 0;
      } else if (head.localName == 'h3') {
        className = 'toc03';
        num03++;
        numText = num02 + '.' + num03;
        num04 = 0;
      } else if (head.localName == 'h4') {
        className = 'toc04';
        num04++;
        numText = num02 + '.' + num03 + '.' + num04;
        num05 = 0;
      } else if (head.localName == 'h5') {
        className = 'toc05';
        num05++;
        numText = num02 + '.' + num03 + '.' + num04 + '.' + num05;
        num06 = 0;
      } else if (head.localName == 'h6') {
        className = 'toc06';
        num06++;
        numText = num02 + '.' + num03 + '.' + num04 + '.' + num05 + '.' + num06;
      }

      // hタグにアンカーを付与
      head.innerHTML += `<span id="head${i}"></span>`;
      contents += `<li class="${className}"><a href="#head${i}"><span>${numText}</span>${head.textContent}</a></li>`;
    })
    document.querySelector('#toc').innerHTML += `<ul>${contents}</ul>`;
  }

      // アンカー・スムーススクロール
      const offset = 0; // 固定ヘッダーの場合、ヘッダーの高さを入れる
      const links = document.querySelectorAll('a[href^="#"]'); //すべてのaタグ href属性に#が入ってるもの
      console.log(links);
      // DOM内のa[href^="#"]の数だけループ
      links.forEach((link) => {
        // a[href^="#"]をクリックしたら
        link.addEventListener('click', (event) => {
          event.preventDefault();
          //href属性の中身を取得
          let href = link.getAttribute('href').substr(1);
          href = href == '#' ? 'html' : `[id="${href}"]`;
          //href属性のIDを変数に格納    
          const target = document.querySelector(href);
          //上記IDの位置を取得
          const rect = target.getBoundingClientRect();
          //現在のスクロール量を取得
          const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
          //固定ヘッダーの高さを引く
          const position = rect.top + scrollTop - offset;
          //スムーススクロール
          window.scrollTo({
            top: position,
            behavior: 'smooth'
          });
        });
      });
});

スムーススクロールさせる場合のコード例です。

前章に追加でscrollToメソッドを定義。

アンカーリンクの場合にスムーススクロールさせるようにします。

スムーススクロールの実装方法について詳しくはこちらを参照ください。
https://kasumiblog.org/js-smooth-scroll/

任意の箇所へ目次を動的に移動

WordPressなど最初のhタグ上などに目次を移動したいなどありますよね?

目次を任意の箇所へ動的に移動したい場合はinsertBeforeなどを使うと便利です。

insertBeforeの使い方について詳しくはこちらを参照ください。
https://kasumiblog.org/javascript-dom-move/

まとめ

JavaScriptを使い、目次をプラグイン無しで自動生成する方法について紹介しました。

以上で解説を終わります。

目次

関連記事

JavaScript

Failed to execute ‘insertBefore’ on ‘Node’ The node before which the new node is to be inserted is not a child of this node.の原因・対処方法

2022.12.03
264
JavaScript

スクロール中、要素をふわっと出す。scrollrevealjs の使い方について【簡単便利】

更新日:2022.09.09
244
JavaScript

JavaScriptで文字列と変数を組み合わせて出力する二つの方法

2022.11.28
128
JavaScript

【簡単】videoタグの再生速度を調整(早く・遅く)する方法

更新日:2022.09.09
1173