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

# やりたきこと
見出しタグ（h1～h6）を元に動的にアンカーリンク付きの目次を作成＆表示する。

## 実装(HTML)
以下のような目次と見出しを用意します。

```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）を読み取って目次を作成します。

```javascript
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122485/1828e457-17f9-1571-09c0-8a7865a69491.png)

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

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

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

## 実装(JavaScript)

```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>`;
    }
});
```

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

## 実装(CSS)

```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;
}
```

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

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

![image33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122485/66c2e801-2887-a4e7-9052-0d23c80bfd8a.png)

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