記事に目次をつけよう

目次があれば、結末まですぐに飛ぶことができる。

時間に追われる現代人にはなくてはならない機能である。

そこで本サイトに目次を導入した。

プラグインは使いたくないよう

個人的にプラグインには頼りたくない。

理由はシンプルで楽しくないからである。

どうせ作るなら自分自身の力で作成したい。

なので今回も自作した。

目次を挿入するための考え方

やりたいことはページが開かれた時に記事に目次を挿入する。

で、対象はh2タグとh3タグとする。

つまり、記事内にh2タグとh3タグがあるかを判定し、あればそれに対応する目次を挿入する。

実現コードはこちら

// 目次を生成
let secondIdCount = 1
let ThirdIdCount = 1
const headingSecond = document.querySelector('.article-detail')?.querySelectorAll('h2')
const headingThird = document.querySelector('.article-detail')?.querySelectorAll('h3')
if (headingSecond !== undefined || headingThird !== undefined) {

    headingSecond.forEach(el => {
        el.id = `tocSecond-${secondIdCount}`
        secondIdCount++
    });
    headingThird.forEach(el => {
        el.id = `tocThird-${ThirdIdCount}`
        ThirdIdCount++
    });

    const target = document.querySelectorAll(`[id^='toc']`)

    const insertTarget = document.querySelector('.article-table-of-contents-insert')
    if (target.length === 0) {
        document.querySelector('.article-table-of-contents').style.display = 'none'
    } else {
        target.forEach((el) => {
            if (el.nodeName === 'H2') {
                insertTarget.insertAdjacentHTML('beforeend', `<p class="second" onclick="smoothScroll('${el.id}')">${el.innerText}</p>`)
            } else {
                insertTarget.insertAdjacentHTML('beforeend', `<p class="third" onclick="smoothScroll('${el.id}')">${el.innerText}</p>`)
            }
        });
    }
}

// スムーススクール
const smoothScroll = (el) => {
    const box = document.querySelector('.content')
    const scrollY = box.scrollTop
    const target = document.getElementById(el).getBoundingClientRect().top
    const headerHight = document.getElementById('header').clientHeight
    box.scrollTo({
        top: target - headerHight + scrollY,
        behavior: 'smooth'
    })
}

前提として記事内に、

<div class="article-table-of-contents">
  <p class="title">目次</p>
  <div class="article-table-of-contents-insert">


  </div>
</div>

という要素を書いておく必要がある。

実現コードを書き下してみる

事前に必要な情報を定義する

// 目次を生成
let secondIdCount = 1
let ThirdIdCount = 1
const headingSecond = document.querySelector('.article-detail')?.querySelectorAll('h2')
const headingThird = document.querySelector('.article-detail')?.querySelectorAll('h3')

まずは諸々定義する。

IDに連番を振るので、基準の数字。

記事内にある、h2タグとh3タグ。

連番IDを付与する

headingSecond.forEach(el => {
    el.id = `tocSecond-${secondIdCount}`
    secondIdCount++
});
headingThird.forEach(el => {
    el.id = `tocThird-${ThirdIdCount}`
    ThirdIdCount++
});

const target = document.querySelectorAll(`[id^='toc']`)

事前に定義した情報をもとにh2タグh3タグに連番IDを振る。

IDを振った要素を取得する。

対象を目次に挿入していく

const insertTarget = document.querySelector('.article-table-of-contents-insert')
if (target.length === 0) {
    document.querySelector('.article-table-of-contents').style.display = 'none'
} else {
    target.forEach((el) => {
        if (el.nodeName === 'H2') {
            insertTarget.insertAdjacentHTML('beforeend', `<p class="second" onclick="smoothScroll('${el.id}')">${el.innerText}</p>`)
        } else {
            insertTarget.insertAdjacentHTML('beforeend', `<p class="third" onclick="smoothScroll('${el.id}')">${el.innerText}</p>`)
        }
    });
}

まず、挿入する要素を取得する。

次に先ほど取得したtagertが、H2であればh2に紐づくHTMLを。

H3であればh3に紐づくHTMLをどんどん挿入していく。

クリック時に対象要素にスクールする

// スムーススクール
const smoothScroll = (el) => {
    const box = document.querySelector('.content')
    const scrollY = box.scrollTop
    const target = document.getElementById(el).getBoundingClientRect().top
    const headerHight = document.getElementById('header').clientHeight
    box.scrollTo({
        top: target - headerHight + scrollY,
        behavior: 'smooth'
    })
}

上記はヘッダーの高さを取得しているので、そこは適宜変更してほしい。

技術を書く技術がない

うーん。

色々書いたが、うまく伝えられない。

考え方さえわかれば難しくないと思うので、挑戦してみてほしい。

わからなければコメントを残してもらえれば個人的に解説をするので、どんどんコメントしてください。