LoginSignup
1

More than 1 year has passed since last update.

posted at

【JavaScript】動的に目次を作成する

やりたきこと

見出しタグ(h1~h6)を元に動的にアンカーリンク付きの目次を作成&表示する。

実装(HTML)

以下のような目次と見出しを用意します。

<details id="table-of-content" open>
    <summary>Table of Contents</summary>
</details>

<h1>Content 1</h1>
<p>Lorem ipsum ...</p>

<h2>Content 2</h2>
<p>Lorem ipsum ...</p>

<h3>Content 3</h3>
<p>Lorem ipsum ...</p>

<h4>Content 4</h4>
<p>Lorem ipsum ...</p>

<h5>Content 5</h5>
<p>Lorem ipsum ...</p>

<h6>Content 6</h6>
<p>Lorem ipsum ...</p>

<details></details> 内に各見出しへのアンカーリンクをJavaScriptで動的に作成します。

実装(JavaScript)

見出しタグ(h1~h6)を読み取って目次を作成します。

document.addEventListener('DOMContentLoaded', () => {
    const heads = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
    if (heads && heads.length) {
        let contents = '';
        heads.forEach((head, i) => {
            contents += `<li><a href="#head${i}">${head.textContent}</a></li>`;
            head.innerHTML += `<a id="head${i}"></a>`;
        })
        document.querySelector('#table-of-content').innerHTML += `<ol>${contents}</ol>`;
    }
});

見出しを読み取る必要があるので、実行のタイミングは DOMContentLoaded にします。
querySelectorAll() はカンマ区切りとすることでタグを複数指定&取得できます。

取得した見出しの数だけ繰り返し、

  • 目次のアンカータグ
  • 各見出しに対応するアンカータグ

の2つを作成します。
最後に <summary>Table of Contents</summary> の直下に作成した目次のアンカータグを突っ込んだら目次の完成です。

表示確認

ブラウザで確認してみましょう。
image32.png

各見出しへのリンクが表示されました!
ただ、スタイルを何も当てていない目次の見栄えがよろしくないですね・・改善しましょう。

改善する

目次に対して以下の改善を行います。

  • 番号を除去する
  • 下線を除去する
  • 段落を下げて階層を分ける

実装(JavaScript)

document.addEventListener('DOMContentLoaded', () => {
    const heads = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
    if (heads && heads.length) {
        let contents = '';
        heads.forEach((head, i) => {
            let className = '';
            switch(head.localName) {
                case "h1":
                    className = `contents1`;
                    break;
                case "h2":
                    className = `contents2`;
                    break;
                case "h3":
                    className = `contents3`;
                    break;
                case "h4":
                    className = `contents4`;
                    break;
                case "h5":
                    className = `contents5`;
                    break;
                case "h6":
                    className = `contents6`;
                    break;
            }
            contents += `<li><a class="${className}" href="#head${i}">${head.textContent}</a></li>`;
            head.innerHTML += `<a id="head${i}"></a>`;
        })
        document.querySelector('#table-of-content').innerHTML += `<ol>${contents}</ol>`;
    }
});

取得したタグが h1h6 のいずれであるかを判定してクラス名として付与します。

実装(CSS)

li {
    list-style: none;
}
a {
    text-decoration: none;
}
.contents1 {
    margin-left: 0px;
}
.contents2 {
    margin-left: 15px;
}
.contents3 {
    margin-left: 30px;
}
.contents4 {
    margin-left: 45px;
}
.contents5 {
    margin-left: 60px;
}
.contents6 {
    margin-left: 75px;
}

lia タグのスタイルを除去、
付与したクラスにはそれぞれ左側にmarginを設けて階層っぽく見えるようにします。

表示確認

ブラウザで確認してみましょう。

image33.png

無駄な番号や下線も消え、階層化されました。
今回は h1h6 が1つずつで試していますが、見出しが増えるとより自然に見えるかと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1