JavaScriptの学習 目次の作成 目次の階層化 ページ内リンクの設定 パラメータの引数化

2022/10/17

JavaScript

目次
JavaScript 目次 階層化 ページ内リンク パラメータ

目次リストを作成するJavaScriptをアップデート

以前のブログの記事『リスキリング JavaScriptの学習 目次の作成』にて紹介した、目次リストを作成するJavaScriptをアップデートしました。
  • 目次リストを作成するJavaScriptの機能
    • 見出しタグ(Hタグ)に基づいて目次を階層化
    • 目次にページ内リンクを設定
    • 目次を設置するHTMLタグのIDを引数化
    • ブログから抽出する見出しタグ(Hタグ)を引数化

使い方 目次の作成

目次を作成するJavaScriptの使い方は次の通りです。

ブログ内の目次を設置したい場所に次のマークを記述します。
<div id="TocTop">目次</div>

HTMLタグはDIV以外でも良く、IDが重要です。
また、IDの設定値は、JavaScriptの引数を使用することで変更できます。

さらに、ブログ内の最後にJavaScriptに関する記述を追加します。
<script>
  var TocHtmlId = 'MyTocTop';
  var HTagType = 'H2, H3, H4';
</script>

<script src="https://4affiuser.github.io/src/Toc3.js"></script>

変数TocHtmlIdは、目次を設定するHTMLタグのIDを設定します。
変数の宣言を省略した場合は、TocTopが設定されます。

また、変数HTagTypeは、参照する見出しタグの種類を設定します。
変数の宣言を省略した場合は、H3とH4が設定されます。

アルゴリズム 目次の作成

目次を作成するJavaScriptに関するアルゴリズムは次の通りです。
  1. ブログ内の見出しタグを検索
  2. 見出しタグの種類に基づいて、ULタグを追加し、目次を階層化
  3. 見出しタグにIDを割り振り、このIDに基づいてページ内リンクを設置
  4. 作成した目次を予めブログ内に設置した目次のマーク(印)に追加

JavaScriptソースコード 目次の作成

目次の作成用JavaScriptソースコードを次に記載します。
// 目次を設置するHTMLタグのIDを確認
if ('undefined' === typeof TocHtmlId) {
    //
    // HTMLタグのIDが指定されていない場合
    //

    // HTMLタグのIDとして'TocTop'を設定
    var TocHtmlId = 'TocTop';
}

// 抽出する見出しタグの種類を確認
if ('undefined' === typeof HTagType) {
    //
    // 抽出する見出しタグの種類が指定されていない場合
    //

    // 抽出する見出しタグの種類としてH3, H4タグを設定
    var HTagType = 'H3, H4';
}

// 文字列を大文字に変換
HTagType = HTagType.toLocaleUpperCase();

// 文字列内の' 'を削除し、コンマで分割した配列を生成
let HTagTypeAry = HTagType.replace(/ /g,'').split(',');

// 配列内の重複した要素を削除
HTagTypeAry = Array.from(new Set(HTagTypeAry));

// 見出しタグの順序を昇順に並び替え
HTagTypeAry.sort(
    function(a, b) {
        if (a < b) return -1;
        else return 1;
    }
);

// 配列を文字列に変更
HTagType = HTagTypeAry.join(',');

// 目次を設置するHTMLタグを抽出
let TocTop = document.getElementById(TocHtmlId);

// HTMLタグ(ID = TocHtmlId)の存在を確認
if (null !== TocTop) {
    //
    //HTMLタグ(ID = TocHtmlId)が存在する場合
    //

    // 見出しタグを含む文を抽出し、NodeList化
    let TagList = document.body.querySelectorAll(HTagType);

    //
    // 目次リストを作成
    //

    // ブログ内の見出しタグの存在を確認
    if (0 < TagList.length) {
        //
        // 見出しタグが含まれている場合
        //

        // 目次リスト用HTML変数にULタグを追加
        let TocHtmlTop = document.createElement('ul');

        // 目次リスト用HTMLのノードポインタ
        let CurrentNode = TocHtmlTop;

        // 見出しタグのID用カウンタ
        let HTagIdCounter = 0;

        // 1つ前の見出しタグの階層
        let PreHTagLayer = 0;

        TagList.forEach(
            // 見出しタグの個数だけ繰り返し
            function(element) {
                // 見出しタグのIDの存在を確認
                if ('' === element.id) {
                    //
                    // 見出しタグにIDが存在しない場合
                    //

                    // 見出しタグにIDを設定
                    element.id = 'HTagId-' + HTagIdCounter;

                    // ID用カウンタのインクリメント
                    HTagIdCounter++;
                }

                // 見出しタグの階層を確認(H2: 0, H3: 1, H4: 2, ...)
                const HTagLayer = HTagTypeAry.indexOf(element.tagName);

                // 見出しタグの階層が繰り上がったため、各変数を初期化
                if (HTagLayer < PreHTagLayer) {
                    // ノードポインタの階層を初期化
                    CurrentNode = TocHtmlTop;

                    // 1つ前の見出しタグの階層情報を初期化
                    PreHTagLayer = 0;
                }

                // 見出しタグの階層に従ってULタグを追加
                if (HTagLayer !== PreHTagLayer) {
                    // ULタグ追加処理ループ(Ex. H2 → H4を想定)
                    for (let i = PreHTagLayer; i < HTagLayer; i++) {
                        // ULタグを追加
                        let TmpTag = document.createElement('ul');

                        // ULタグをノードポインタの最後の子要素として追加
                        CurrentNode.appendChild(TmpTag);

                        // ノードポインタの階層を更新
                        CurrentNode = CurrentNode.lastElementChild;
                    }
                }

                // LIタグを追加
                let TocLiTag = document.createElement('li');

                // Aタグを追加
                let TocATag = document.createElement('a');
                // 見出しタグの内容
                TocATag.innerHTML = element.textContent;
                // ページ内リンクのジャンプ先
                TocATag.href = '#' + element.id;
                
                // LIタグの子要素としてAタグを追加
                TocLiTag.appendChild(TocATag);

                // LIタグをノードポインタの最後の子要素として追加
                CurrentNode.appendChild(TocLiTag);

                // 1つ前の見出しタグの階層情報を更新
                PreHTagLayer = HTagLayer;
            }
        );

        // 目次リストをHTMLに追加
        TocTop.appendChild(TocHtmlTop);
    }
}
JavaScriptソースコードのポイントを以下に記載します。

行番号2~9では、目次を設置するHTMLタグのIDを設定する変数を定義しています。
ブログ内で下記のように変数が宣言されているかを確認し、宣言されていなければ、デフォルト値としてTocTopが変数TocHtmlIdに代入されます。
<script>var TocHtmlId = 'abcd';</script>

行番号12~19では、目次を作成するために参照する見出しタグの種類を設定する変数を定義しています。
ブログ内で下記のように変数が宣言されているかを確認し、宣言されていなければ、デフォルト値としてH3とH4が変数HTagTypeに代入されます。
<script>var HTagType = 'H2, H3, H4';</script>

行番号22~36では、設定された見出しタグの文字列を成形する処理を行っています。
例えば、次のように変換します。
  • 'h2, H3' → 'H2, H3'(小文字を大文字に変換)
  • 'H3, H4, H2' → 'H2, H3, H4'(昇順に並べ替え)
  • 'H2, H3, H4, H3' → 'H2, H3, H4'(重複を修正)

行番号79~89では、ページ内リンクを作成するためのIDを見出しタグに設定しています。

行番号92では、処理の対象である見出しタグの階層を抽出しています。
例えば、抽出対象の見出しタグがH2, H3, H4である場合は、次のようになります。
  • H2タグ → 階層0
  • H3タグ → 階層1
  • H4タグ → 階層2

行番号95~101では、処理の対象である見出しタグの階層が、一つ前に処理をした見出しタグの階層より上位に移動した場合、変数の初期化を行っています。
例えば、見出しタグの処理がH2→H3→H4→H2と行われた場合、H4→H2の段階で変数の初期化を行います。

行番号104~116では、処理の対象である見出しタグの階層に基づいて、ULタグを作成しています。
例えば、見出しタグの階層がH2→H4へとH3を飛ばして変化した場合、行番号106のfor文にて、2つのULタグを作成しています。

行番号119~126では、見出しタグに基づいたLIタグとページ内リンクのAタグを作成しています。

目次の階層化に関する親子関係の処理

目次のリストを階層化する処理では、リストの親子関係に基づく次のようなことを行っています。

H2→H4の処理

目次のリストのルート(始点)は、変数TocHtmlTopです。
行番号64: let TocHtmlTop = document.createElement('ul');

また、ルートから親子関係をたどるために変数CurrentNodeを使用しています。
行番号67: let CurrentNode = TocHtmlTop;

見出しタグの処理がH2→H4では、順に次の処理を行います。
  1. ULタグを作成(行番号108)
  2. CurrentNodeの子要素の最後にULタグを追加(行番号111)
  3. CurrentNodeをCurrentNodeの子要素の最後に更新(行番号114)
  4. LIタグを作成(行番号119)
  5. Aタグを作成(行番号122)
  6. LIタグの子要素にAタグを追加(行番号129)
  7. CurrentNodeの子要素の最後にLIタグを追加(行番号132)

H4→H3の処理

また、H4→H3のように階層が上位に移動した場合は、順に次の処理を行います。
  1. 階層が上位に移動したため、CurrentNodeを初期化(行番号97)
  2. ULタグを作成(行番号108)
  3. CurrentNodeの子要素の最後にULタグを追加(行番号111)
  4. CurrentNodeをCurrentNodeの子要素の最後に更新(行番号114)

以降の処理は、H2→H4の場合と同じです。

自己紹介

自分の写真
株式トレード歴は15年以上、専業株式トレーダーになって5年以上、今も現役で活動中

Blogger. | Designed by QooQ.