はじめに
以下をCocoonで実現します。
完成イメージ

実装手順
以下のコードをfunctions.phpに追加します。
// メタボックスを追加
add_action('add_meta_boxes', function () {
add_meta_box(
'toc_exclude',
'目次から除外する見出し',
'toc_exclude',
'post',
'side'
);
});
/**
* メタボックスの入力フォームを表示
*
* @param WP_Post $post 投稿オブジェクト
* @return void
*/
if (!function_exists('toc_exclude')):
function toc_exclude($post) {
wp_nonce_field('toc_exclude_action', 'toc_exclude_nonce');
// 投稿本文を取得し見出しを抽出
$content = get_post_field('post_content', $post->ID);
preg_match_all('/<h[2-6].*?>(.*?)<\/h[2-6]>/is', $content, $matches);
// 保存済みの除外IDリストを取得
$excluded = get_post_meta($post->ID, 'my_toc_excluded_ids', true) ?: [];
if (!empty($matches[1])) {
// 見出しごとにチェックボックス生成
foreach ($matches[1] as $index => $title) {
$id = 'toc' . ($index + 1);
$checked = in_array($id, $excluded, true) ? 'checked' : '';
?>
<div style="margin-bottom:5px;">
<label>
<input
type="checkbox"
name="toc_exclude[]"
value="<?= esc_attr($id); ?>"
<?= $checked; ?>
>
<?= esc_html(strip_tags($title)); ?>
</label>
</div>
<?php
}
} else {
?>
<p>見出しが見つかりません。</p>
<?php
}
}
endif;
// 投稿データを保存
add_action('save_post', function ($post_id) {
// nonceチェック
if (!isset($_POST['toc_exclude_nonce']) || !wp_verify_nonce($_POST['toc_exclude_nonce'],
'toc_exclude_action')) {
return;
}
// 自動保存チェック
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// リビジョンは除外
if (wp_is_post_revision($post_id)) {
return;
}
// 権限チェック
if (!current_user_can('edit_post', $post_id)) {
return;
}
// メタデータ保存
if (isset($_POST['toc_exclude']) && is_array($_POST['toc_exclude'])) {
$clean_ids = array_map('sanitize_text_field', $_POST['toc_exclude']);
update_post_meta($post_id, 'my_toc_excluded_ids', $clean_ids);
} else {
delete_post_meta($post_id, 'my_toc_excluded_ids');
}
});
// フロント用JS・CSSを追加
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
$post_id = get_the_ID();
// 除外IDを取得
$excluded_ids = get_post_meta($post_id, 'my_toc_excluded_ids', true);
if (empty($excluded_ids) || !is_array($excluded_ids)) {
return;
}
$css = '';
foreach ($excluded_ids as $id) {
$css .= '.toc-content li:has(> a[href="#' . esc_attr($id) . '"]) { display: none !important; }' . PHP_EOL;
}
if ($css) {
$handle = 'toc_exclude';
wp_register_style($handle, false);
wp_enqueue_style($handle);
wp_add_inline_style($handle, $css);
}
});
このコードの運用上の懸念点と目的の不明瞭さ
本カスタマイズを検討する際、真っ先に直面するのが「そもそも、どのシーンで使うべきなのか?」という問いです。
参考ページでは具体的な目的が語られず、ただ「非表示にする方法」だけが提示されていますが、実運用においては以下の問題が生じます。
ユーザーに対する「地図」としての役割の欠如
目次は、ユーザーが記事の全体像を把握し、必要な情報へ素早くアクセスするための「地図」です。
ユーザーは目次を見ることで「この記事には何が書かれており、どこを読めば自分の悩みが解決するか」を瞬時に判断します。
特定の項目を恣意的に非表示にする行為は、この地図から重要なランドマークを勝手に削除することに等しく、ユーザー体験を損なう可能性があります。
もし単に「見出しの見た目が不格好だから」「文言が長いから」という理由だけで隠すのであれば、それは目次から情報を隠すのではなく、「ユーザーに分かりやすい文言へ書き換える」ことこそが本筋です。
懸念される問題点
以下のような問題点が懸念されます。
「二度手間」が発生する執筆フロー
本手法は、記事を一度保存して見出しIDを生成させた後、メタボックスを確認して除外対象にチェックを入れるという作業が必須です。
記事構成が頻繁に変わる執筆段階では、更新のたびに保存とチェックのやり直しが発生し、作業効率を著しく低下させます。
見出しの順序変更によるリスク
このコードは「記事の上から何番目の見出しか」というインデックス(toc1, toc2…)に基づいて除外を行います。
執筆の途中で見出しを並び替えたり、新規追加して再度保存した場合、以前設定した「除外ルール」が別の見出しに適用される可能性があります。
最終公開前の「最終検品」として再チェックが必須となります。
さいごに
今回紹介した方法を使えば、システムやデザインの制約でどうしても消せない「目次のノイズ」をピンポイントで除去し、読者にとってより読みやすい記事構造へと最適化できます。
ただし、本手法はあくまで「緊急避難策」です。
目次を綺麗にするための最優先事項は、見出し構成を根本から見直し、不要な要素を削ぎ落とすことにあるという点を忘れないでください。

