WordPressサイトで、ブログ記事や、企業サイト・サービスサイトにおけるコラムなどでよく使用されている「目次機能」。便利なプラグインもたくさんありますが、ベーシックなもので良いということであれば、設定を管理画面から度々変更していくものでもないので、プラグインに頼らず自作にトライするのもオススメです。
またコンテンツの目次の仕組みを理解することは、HTML・SEOの知見を深めることにも繋がります。
プラグインなしでつくる目次のコード全体
// 目次の中身
function generate_table_of_contents($headings) {
$table_of_contents = '<div class="toc"><p class="tocTitle">目次</p><ol class="tocList">';
$current_h2_item = '';
$has_h3 = false;
foreach ($headings as $heading) {
if ($heading['tag'] == 'h2') {
if ($current_h2_item !== '') {
if ($has_h3) {
$table_of_contents .= '</ol>';
}
$table_of_contents .= '</li>';
}
$current_h2_item = '<li class="item-h2"><a href="#' . $heading['id'] . '">' . $heading['text'] . '</a>';
$has_h3 = false;
$table_of_contents .= $current_h2_item;
} else {
if (!$has_h3) {
$table_of_contents .= '<ol class="toc_sublist">';
$has_h3 = true;
}
$table_of_contents .= '<li class="item-h3"><a href="#' . $heading['id'] . '">' . $heading['text'] . '</a></li>';
}
}
if ($current_h2_item !== '') {
if ($has_h3) {
$table_of_contents .= '</ol>';
}
$table_of_contents .= '</li>';
}
$table_of_contents .= '</ol></div>';
return $table_of_contents;
}
// 目次の挿入
function insert_table_of_contents($content) {
if (is_singular('post')) {
// $heading_pattern = '/<(h2)(.*?)>(.*?)<\/(h2)>/is';
$heading_pattern = '/<(h2|h3)(.*?)>(.*?)<\/(h2|h3)>/is';
$matches = array();
$heading_count = preg_match_all($heading_pattern, $content, $matches, PREG_SET_ORDER);
if ($heading_count >= 2) {
$headings = array();
$heading_counter = 0;
foreach ($matches as $match) {
$headings[] = array(
'tag' => $match[1],
'attributes' => $match[2],
'text' => $match[3],
'id' => 'heading-' . $heading_counter
);
$heading_counter++;
}
$content = preg_replace_callback($heading_pattern, function ($match) use ($headings) {
static $index = 0;
$id = $headings[$index]['id'];
$index++;
return sprintf('<%1$s%2$s><span id="%3$s">%4$s</span></%1$s>', $match[1], $match[2], $id, $match[3]);
}, $content);
$table_of_contents = generate_table_of_contents($headings);
$first_h2_position = strpos($content, '<h2');
if ($first_h2_position !== false) {
$content = substr_replace($content, $table_of_contents, $first_h2_position, 0);
}
}
}
return $content;
}
add_filter('the_content', 'insert_table_of_contents');
コードの大まかな流れ
①目次の中身を作るgenerate_table_of_contents($headings)
という関数を用意し、②記事などに目次を挿入させるinsert_table_of_contents($content)
という関数内で①の関数を実行します。
目次の中身をつくる関数の解説
引数$headings
は記事内のHタグ情報を配列にしたものを想定しており、以下のような配列を②の関数内で$headings
に入れています。今回のスクリプトは目次に反映させるHタグは、H2、H3です。
Array
(
[0] => Array
(
[tag] => h2
[text] => タイトルテキストが入ります
[id] => heading-0
)
[1] => Array
(
[tag] => h3
[text] => タイトルテキストが入ります
[id] => heading-1
)
[2] => Array
(
[tag] => h3
[text] => タイトルテキストが入ります
[id] => heading-2
)
[3] => Array
(
[tag] => h2
[text] => タイトルテキストが入ります
[id] => heading-3
)
)
$table_of_contents
に出力するHTMLを格納します。処理の過程で.=
による文字列結合を行い目次の中身を作成しています。
H2配下にH3があるかないかで処理を変更するため、$current_h2_item
と$has_h3
を最初に定義しておきます。
$table_of_contents = '<div class="toc"><p class="tocTitle">目次</p><ol class="tocList">';
$current_h2_item = '';
$has_h3 = false;
8行目でforeach文で繰り返し処理を行って$heading
の値を利用し目次を作成しています。
foreach ($headings as $heading) {
if ($heading['tag'] == 'h2') {
if ($current_h2_item !== '') {
if ($has_h3) {
$table_of_contents .= '</ol>';
}
$table_of_contents .= '</li>';
}
$current_h2_item = '<li class="item-h2"><a href="#' . $heading['id'] . '">' . $heading['text'] . '</a>';
$has_h3 = false;
$table_of_contents .= $current_h2_item;
} else {
if (!$has_h3) {
$table_of_contents .= '<ol class="toc_sublist">';
$has_h3 = true;
}
$table_of_contents .= '<li class="item-h3"><a href="#' . $heading['id'] . '">' . $heading['text'] . '</a></li>';
}
}
今回は、H2とH3を②の関数内で拾うので、9行目のif($heading['tag']=='h2'){ }
でH2タグの場合の処理を行い、20行目のelse {}
でH3タグの場合の処理を記述します。
繰り返しおける最初の処理は16行目になります。$current_h2_item
はH2タグを目次に反映させるコードが入ります。H3タグは、H2タグの下に入るようにしたいので、その場合に備えてこの段階ではliタグが閉じられていません。$has_h3
はH2の処理をしている最中なのでfalse
にしておきます。
H3タグの際には、H2配下における1つ目のH3か、2つ目以降のH3かを21行目で$has_h3
のフラグを利用して判別し処理を変えています。1つめの時はリストの開始タグ<ol class="toc_sublist">
を入れ、2つ目以降はこの処理を行いません。
このH3のol
の閉じタグは、次のH2タグが出る時です。それは、$heading['tag']=='h2'
で$current_h2_item
に値があり$has_h3
がtrue
の時、つまり21行目のことです。