5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

やりたきこと

見出しタグ(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つずつで試していますが、見出しが増えるとより自然に見えるかと思います。

5
2
0

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
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?