MENU

【WordPress高速化】目次の高さを見出しの数で自動設定

【WordPress高速化】目次の高さを見出しの数で自動設定
目次

はじめに

「WordPress高速化」シリーズの第5回です。【WordPress高速化】SWELLのCLSをCSSで改善(以下「第2回の記事」)では、SWELLの目次に min-height で高さを確保して、ページのずれ(CLS)を減らしました。

ただ、この方法では目次の高さをどの記事でも同じ値にしています。見出しが少ない記事では、目次の下に大きな余白ができます。第2回の記事のよくある質問で「記事ごとに目次の高さを自動で変える方法は、今後の回で紹介する」と書いたのが、今回の内容です。

この記事では、次の2つを組み合わせます。

  • PHP:本文の見出しを数えて、その数を --toc-n というCSS変数として出力する
  • CSS:「1項目の高さ×項目数」の式で、目次の min-height を計算する

また、当サイトの記事で実際に測ったところ、スマホでは見出しの折り返しで目次の高さが変わり、第2回の記事の値を超える記事がありました。この問題は、目次の見出しを1行に収めて「…」で省略することで解決します。

この記事のコードは、SWELLの子テーマの functions.php と、カスタマイザーの「追加CSS」を使います。編集する前に、必ずバックアップを取ってください。

目次の高さは何で決まるか

SWELLの目次は、ページを開いたあとにJavaScriptで中身が作られます。中身が入るまで、目次の高さはほぼ0です。そのため、min-height で先に高さを確保しておく必要があります。

目次の高さは、カスタマイザーの「目次」の設定で決まります。当サイトの設定は次のとおりです。

設定当サイトの値SWELLの初期値
どの階層の見出しまで抽出するかh2h3
見出し何個以上で表示するか2個2個
目次の省略表示項目数で省略省略しない
項目が何個を超えると省略するか5個15個

SWELLのJavaScriptを読んで、省略のしかたを確認しました。

  • 目次の項目は、h2もh3もまとめて数える
  • 「5個を超えると省略」の設定では、6項目までは全部表示される(設定画面の注意書きにある「指定の数+1」)
  • 7項目以上になると、5項目と「もっと見る」ボタンの表示になる
  • 見出しが「何個以上で表示するか」に届かない記事には、目次が出ない

つまり、目次の高さは「表示される項目数」と「もっと見るボタンがあるか」の2つで決まります。当サイトの設定では、2〜6項目(全部表示)と、5項目+ボタン(7項目以上)の6通りです。

「省略する数」を5以外にした場合も、考え方は同じです。たとえば3にすると、4項目までは全部表示され、5項目以上は「3項目+ボタン」になります。

見出しの数ごとに目次の高さを測る

当サイトの公開記事179本で、目次の高さを測りました。広告の読み込みは止め、第2回の記事の min-height も外して、目次の本来の高さを測っています。

パソコン:項目数だけで高さが決まる

目次の項目数234567以上
目次の高さ170.9px204.6px238.2px271.8px305.4px313.2px

項目が1つ増えるごとに、約33.6px高くなります。7項目以上は「5項目+もっと見る」なので、何個あっても313.2pxです。これは、5項目の271.8pxに、ボタンの高さ(約41.5px)を足した値です。

第2回の記事では、313pxに固定していました。たとえば項目が3つの記事では、目次の下に約108pxの余白ができます。当サイトでは、50px以上の余白が出ている記事が35本ありました。

目次の高さを313pxに固定した場合と、項目数に合わせた場合の比較。項目が3つの記事では、固定だと目次の下に約108pxの余白ができる。項目数に合わせると余白がなくなる

スマホ:見出しの折り返しで高さが変わる

スマホ(幅375px)では、目次の見出しが長いと2行に折り返します。1行折り返すごとに、目次が24px高くなりました。

  • 7項目以上の記事でも、折り返しの数によって274.6px〜370.6pxと、最大で96pxの差がある
  • 第2回の記事で設定した314.84pxを超える記事が23本あった(最大で56px超過)

PHPで数えられるのは見出しの数だけで、スマホで何行に折り返すかまでは分かりません。そこで、目次の見出しを折り返さずに1行で表示し、はみ出た部分を「…」で省略することにしました。こうすると、スマホでも目次の高さが項目数だけで決まります。

スマホの目次の比較。見出しが折り返すと、1行ごとに目次が24px高くなり、確保した高さを超えてずれる。1行で省略すると、目次の高さは項目数だけで決まる

省略しても、見出しをタップすればその見出しに移動できます。当サイトの記事では、スマホで1つ以上の見出しが省略される記事が多くありました。目次の見出しが途中で切れるのが気になる場合は、見出しの要点を前のほうに書くと、省略されても内容が伝わりやすくなります。

スマホは画面の幅で高さが変わる

もう1つ分かったことがあります。当サイトのスマホの目次は、画面の幅に比例して大きくなります。

SWELLのカスタマイザー「サイト全体設定」→「基本デザイン」→「フォントサイズ(Mobile)」が「デバイス可変:中」(初期値)になっていると、スマホの文字サイズは画面幅の4%(4vw)になります。幅375pxの画面では15px、幅430pxの画面では17.2pxです。目次の文字や余白も、これに合わせて大きくなります。

そこで、スマホの画面幅を320px〜599pxの13段階で変えて測ったところ、目次の高さは次の式でほぼぴったり表せました(誤差は±0.5px以内)。

目次の項目数目次の高さ(スマホ)幅375pxでの高さ
29.7px+34.3vw138.3px
39.7px+42.7vw169.8px
49.7px+51.1vw201.3px
59.7px+59.5vw232.8px
69.7px+67.9vw264.3px
7以上55.2px+58.5vw274.6px

7項目以上(5項目+ボタン)の式は、5項目の式に、ボタンの高さ(約45.9px-1.1vw)を足した値になっています。スマホでは、ボタンの高さは画面幅によらず、ほぼ40px前後です。

vw は、画面の幅の1%を表す単位です(幅375pxなら1vw=3.75px)。CSSの calc() を使えば、この式をそのまま min-height に書けます。式のpxの部分には、誤差を見込んで少し足しておきます。

なお、画面幅600px以上(タブレット・パソコン)では、文字サイズが固定になるので、どの幅でもパソコンの表と同じ高さでした。

目次の高さを見出しの数で自動設定する方法

仕組みは次のとおりです。PHPで本文の見出しを数えて、その数をCSS変数としてページに出力します。CSSでは、その数を使って目次の高さを計算します。

目次の高さを自動で決める仕組み。①functions.phpで本文の見出しを数える、②head内に--toc-nと--toc-moreのCSS変数が出力される、③追加CSSで項目の高さ×項目数+ボタンの高さを計算してmin-heightにする

Step1:目次の見出しを1行で表示する

カスタマイザーの「追加CSS」に、次のCSSを追加します。

/* 目次の見出しを1行で表示し、はみ出た部分を「…」にする */
.p-toc__list a {
	display: block;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
  • white-space: nowrap:折り返さない
  • overflow: hidden:はみ出た部分を隠す
  • text-overflow: ellipsis:隠した部分を「…」で表す
  • display: block:リンクの幅を目次の幅に合わせる(これがないと「…」が付かない)

このCSSは、画面の幅で分けずに、すべての幅に当てています。幅375px〜1366pxでテストしたところ、パソコンでも画面幅960px前後では長い見出しが折り返したためです。幅1366pxでは、当サイトの記事で省略された見出しはありませんでした。

Step2:functions.phpで見出しを数えてCSS変数を出力する

子テーマの functions.php に、次のコードを追加します。

追記(2026年10月30日)
再利用ブロックやブログパーツの中の見出しも数えるようにし、目次から外した見出しの扱いをSWELLに合わせて、コードを修正しました。以前のコードを入れている場合は、次のコードに置き換えてください。

// 目次に入る見出しの数を数えて、CSS変数 --toc-n・--toc-more を出力する
add_action('wp_head', function () {
	if (!is_singular() || !class_exists('SWELL_Theme')) {
		return;
	}
	// 目次に入る見出しの階層(記事ごとの設定も反映される)
	$target = apply_filters('swell_toc_target', SWELL_Theme::get_setting('toc_target'));
	$max    = (int) substr($target, 1); // 'h3' → 3

	// 再利用ブロックやブログパーツを展開してから、本文の h2〜目次の対象の階層までの見出しを数える
	$content = do_shortcode(do_blocks(get_post_field('post_content', get_queried_object_id())));
	preg_match_all('/<h([2-' . $max . '])\b([^>]*)>/', $content, $m, PREG_SET_ORDER);
	$count = 0; // 目次に入る見出しの数(目次から外す指定のものは除く)
	foreach ($m as $h) {
		if (!str_contains($h[2], 'swl-toc-off')) {
			$count++;
		}
	}

	// 「表示するか」「省略するか」は、SWELLと同じく目次から外した見出しも含めた数で決める
	$all = count($m);
	if (!$count || $all < (int) SWELL_Theme::get_setting('toc_minnum')) {
		return;
	}
	// 省略表示になる場合は、表示される項目数を「省略する数」にして、ボタンありにする
	$more = 0;
	$omit = (int) SWELL_Theme::get_setting('toc_omit_num');
	if (SWELL_Theme::get_setting('toc_omit_type') === 'ct' && $all > $omit + 1 && $count > $omit) {
		$count = $omit;
		$more  = 1;
	}
	echo '<style>:root{--toc-n:' . $count . ';--toc-more:' . $more . '}</style>' . "\n";
});

たとえば当サイトの7項目以上の記事では、ページの <head> に次の1行が出力されます。

<style>:root{--toc-n:5;--toc-more:1}</style>

コードのポイントは次のとおりです。

  • wp_head アクション:ページの <head> の中に、HTMLを出力します。ここで、目次の項目数をCSS変数として出力します
  • --toc-n:目次に表示される項目数です
  • --toc-more:「もっと見る」ボタンがあれば1、なければ0です
  • SWELL_Theme::get_setting():SWELLのカスタマイザーの設定値を読みます。目次の設定を変えても、コードを直す必要はありません
  • swell_toc_target フィルター:記事ごとに「抽出する見出しの階層」を変えた場合も、その設定で数えます
  • do_blocks()・do_shortcode():再利用ブロックやブログパーツを展開して、ページに表示される本文にしてから見出しを数えます。SWELLの目次も、展開したあとの見出しを拾うためです
  • swl-toc-off:SWELLで「目次に表示しない」にした見出しに付くクラスです。この見出しは数えません
  • toc_omit_type が ct:「項目数で省略」の設定です。項目数が「省略する数+1」を超えて省略表示になる記事は、--toc-n を「省略する数」に、--toc-more を1にします。当サイト(省略する数5)の7項目以上の記事なら、--toc-n:5;--toc-more:1 です

見出しの数を「数える」ところまでをPHPで行い、高さの計算はCSSで行います。高さを測り直したときは、追加CSSの式を直すだけで済みます。

Step3:追加CSSに目次の高さの式を書く

「追加CSS」に、次のCSSを追加します。数値は当サイトで測った値です。

/* 目次の高さ = 余白など + 1項目の高さ×項目数 + ボタンの高さ×(0か1) */
/* スマホ:画面幅599px以下 */
@media screen and (max-width: 599px) {
	body .p-toc.-double {
		min-height: calc(10.5px + 17.5vw + 8.4vw * var(--toc-n, 5) + (46px - 1.1vw) * var(--toc-more, 1)) !important;
	}
}
/* タブレット・パソコン:画面幅600px以上 */
@media screen and (min-width: 600px) {
	body .p-toc.-double {
		min-height: calc(104.5px + 33.6px * var(--toc-n, 5) + 42px * var(--toc-more, 1)) !important;
	}
}

式の中身は、次のとおりです。

スマホタブレット・パソコン
余白など(タイトル・枠の余白)10.5px+17.5vw104.5px
1項目の高さ8.4vw33.6px
「もっと見る」ボタンの高さ46px-1.1vw42px
  • たとえば当サイトの7項目以上の記事(--toc-n:5;--toc-more:1)は、パソコンで104.5px+33.6px×5+42px=314.5pxになります
  • 3項目の記事(--toc-n:3;--toc-more:0)なら、ボタンの分は0になり、104.5px+33.6px×3=205.3pxです
  • var(--toc-n, 5) の「5」は、CSS変数が出力されなかったときに使う予備の値です。当サイトの「省略する数」と同じ5と、ボタンありの1にしておくと、目次が最も高くなる場合(5項目+ボタン)の高さになります
  • 画面幅の区切りは、SWELLの文字サイズが切り替わる 599px / 600px にしています
  • 式のpxの部分には、誤差を見込んで約0.8px足しています

項目数ごとに値を書く必要がないので、項目が何個でも、この2つの式だけで対応できます。

「省略する数」を変えた場合

Step2のコードは、省略表示の記事では --toc-n を「省略する数」にします。たとえば「省略する数」が3なら、5項目以上の記事は --toc-n:3;--toc-more:1 になり、目次の高さは「3項目の高さ+ボタンの高さ」で計算されます。

Localで「省略する数」を3・5・7・15(SWELLの初期値)に変え、2〜12項目の記事で測ったところ、ボタンの高さはどの設定でも同じで、Step3の式のままで合いました。15の場合は、12項目まで全部表示され、どの項目数でも式の値で合いました。

省略する数省略表示になる記事目次の本来の高さ(パソコン)Step3の式の値(パソコン)
35項目以上(3項目+ボタン)246.0px246.3px
5(当サイト)7項目以上(5項目+ボタン)313.2px314.5px
79項目以上(7項目+ボタン)380.4px381.7px

「省略する数」を変えても、Step3のCSSは書き換えずに使えます。変えるのは、予備の値の「5」だけです(自分のサイトの「省略する数」に合わせます)。

フォントサイズや目次のデザインによって式の値は変わるので、最後はStep4の方法で確かめてください。

Step4:検証ツールで確認する

  1. 記事を開き、F12で検証ツールを開く
  2. 「要素」パネルで <head> の中を見て、<style>:root{--toc-n:5;--toc-more:1}</style> のような行が出力されているか確認する(Ctrl+Fで --toc-n を検索すると早く見つかります)→下の画像を参照
  3. 目次(div.p-toc.-double)を選び、「スタイル」で min-height に項目数ごとの値が効いているか確認する
  4. 「レンダリング」の「レイアウト シフト領域」をオンにして再読み込みし、目次の下が青く光らないか確認する
検証ツールの要素パネル。headの中に<style>:root{--toc-n:5;--toc-more:1}</style>が出力されている

検証ツールの使い方は、【WordPress高速化】CLSの原因を検証ツールで探す方法で解説しています。

当サイトの公開記事のコピーを使い、テスト用の環境(Local)で、2〜12項目の記事・h3を含む記事・長い見出しの記事を、画面幅320px〜1366pxで確認しました。「省略する数」を3・5・7・15に変えた場合も含め、どの組み合わせでも、目次の本来の高さが min-height を超えることはなく、余白は1.6px以内でした。修正後のコードでも、目次から外した見出しがある記事、再利用ブロックやブログパーツ(ブロック・ショートコード)の中に見出しがある記事を確認し、出力される数がSWELLの目次の表示と一致しました。本番の公開記事179本でも、画面幅360pxと430pxで、目次の高さがこのCSSの値を超える記事はありませんでした。

第2回のCSSとの関係

第2回の記事の目次の指定は、残しておいても、消してもかまいません。

/* 第2回の記事の指定(残しておく) */
@media screen and (min-width: 960px) {
	.p-toc.-double { min-height: 313px !important; }
}
@media screen and (max-width: 959px) {
	.p-toc.-double { min-height: 314.84px !important; }
}

今回の body .p-toc.-double は、body の分だけ詳細度が高いので、どちらにも !important が付いていても、今回の指定が優先されます。

CSS変数が出力されなかったとき(functions.phpのコードを外したときなど)は、今回の式の予備の値(5項目+ボタン)が使われます。今回の指定が常に優先されるので、第2回の指定は使われなくなります。

h3まで抽出する場合

目次に h3 まで入れる設定(SWELLの初期値)でも、Step2のコードはそのまま使えます。h2とh3をまとめて数え、SWELLの目次と同じ項目数を --toc-n に出力します。テスト用の記事で、h2とh3を混ぜた4パターンを確認し、どれもSWELLの目次の表示と一致しました。

ただし、h3の行は、h2の行より少し低くなります(パソコンで h2 が約33.6px、h3 が約26.6px)。そのため、同じ項目数でも、h2とh3の内訳によって目次の高さが変わります。

Step3の式は「全部h2」として測った値なので、h3が混ざると目次は少し低くなります。高さが足りなくなる(ずれる)ことはありませんが、目次の下に余白が出ます。テストでは、パソコンで最大約29px、スマホで最大約43pxでした。h2とh3を分けて数え、それぞれの高さを足せば、この余白もなくせます。この方法は、【WordPress高速化】h3まで入れた目次の高さを自動設定で解説しています。

注意点

  • 表示が少し重くなる場合があります:コードは、本文のブロックをページを作るときにもう一度展開します。ページのキャッシュを使っていれば、この処理が行われるのはキャッシュを作るときだけです
  • ページ分割した記事:<!--nextpage--> で記事を分けると、SWELLの目次はそのページの見出しだけを表示しますが、コードは記事全体の見出しを数えます
  • 目次やフォントサイズの設定を変えたとき:目次のデザイン、フォントサイズ、記事の幅などを変えると、目次の高さも変わります。設定を変えたら、高さを測り直してください(「省略する数」だけを変えた場合は、式はそのままで、予備の値の「5」だけを合わせます)
  • キャッシュ:キャッシュ系のプラグインやSWELLのキャッシュを使っている場合は、コードを追加したあとにキャッシュを削除してください

よくある質問・エラー対処

Q. 自分のサイトの目次の高さは、どうやって測ればよいですか

→ 項目数が違う記事をいくつか開き、検証ツールのコンソールで次のコードを実行します。min-height を一時的に外して、目次の本来の高さを表示します。

// 目次の min-height を外して、本来の高さ(px)を表示する
const toc = document.querySelector('.p-toc.-double');
toc.style.setProperty('min-height', '0', 'important');
toc.getBoundingClientRect().height;

Step3の式の値は、次の3種類の記事を測れば求められます。

  • 2項目の記事と3項目の記事:2つの差が「1項目の高さ」です。2項目の高さから「1項目の高さ×2」を引くと「余白など」になります
  • 省略表示の記事:「省略する数」と同じ項目数の記事との差が「ボタンの高さ」です

スマホのフォントサイズが「デバイス可変」の場合は、検証ツールのデバイスモードで画面幅を 360px と 430px にして2回測ります。2つの高さを h360、h430 とすると、px+vw の式は次のように求められます。

  • vw の値 =(h430 − h360)÷ 70 × 100
  • px の値 = h360 − vw の値 × 3.6

「余白など」の px の値には、1px程度を足して使ってください。フォントサイズが「固定サイズ」の場合は、スマホでも画面幅によらず高さが一定なので、パソコンと同じように px だけで書けます。


Q. CSSの :has() で見出しを数えれば、PHPはいりませんか

→ 目次の中の項目を :has() で数える方法は、ずれの対策には使えません。SWELLの目次は、ページを開いたあとにJavaScriptで作られるので、中身ができるまでは数えられず、そのあいだ高さを確保できないためです。

本文の見出しを :has() で数える方法なら、ページを開いた時点から効きます。ただし、ブロックの中に入れた見出しや、目次から外した見出し、省略表示の条件まで、CSSのセレクタで正しく再現するのは難しくなります。PHPなら、SWELLの設定を読んで数え、ページを送る前に項目数をCSS変数として出力できるので、確実です。


Q. CSS変数が出力されません

→ 次の点を確認してください。

  • コードを、SWELLの子テーマの functions.php に書いているか
  • 見出しが「何個以上で表示するか」の数より少なくないか(少ない記事には、目次もCSS変数も出ません)
  • キャッシュを削除したか

おわりに(まとめ)

まとめ

SWELLの目次の高さは、PHPで見出しを数えてCSS変数に渡せば、記事ごとに自動で確保できます。

  • ✅ 目次の項目数は、h2・h3をまとめて数え、省略表示のときは「省略する数の項目+ボタン」になる
  • ✅ パソコンでは、目次の高さは項目数だけで決まる
  • ✅ スマホでは見出しの折り返しで高さが変わるので、1行で「…」省略にする
  • ✅ スマホの文字サイズが「デバイス可変」なら、高さは px+vw の式で書く
  • ✅ PHPは数えてCSS変数に渡すだけ。高さは「1項目の高さ×項目数+ボタンの高さ」の式で、追加CSSで計算する
  • ✅ CSS変数が出ないときは、式の予備の値(最も高い場合の高さ)が使われる

続きの第6回では、目次にh3まで入れている場合(SWELLの初期値)に、h2とh3を分けて数えて、目次の高さを決める方法を解説しています。

【WordPress高速化】h3まで入れた目次の高さを自動設定

この記事が、少しでも誰かのお役に立てれば幸いです。

関連記事

当サイトの記事で使用したVBAなどのサンプルをDLできます

この記事のサンプルはありません!

ダウンロードページへは下のカードをクリックすればジャンプできます。
よろしければご利用ください!


【CSS】テキスト関連プロパティについて基本解説|Webの基礎


よかったらシェアしてね!
  • URLをコピーしました!
目次