私のブログには、こういうデータベース系の記事が入り込んでいて、目次があった方がいいなと思っていました。そこで、テーマ側に移植してみました。(元ネタ)
以下、コードです。機能自体は、グレードダウンしています。
function make_toc($atts){
$atts = shortcode_atts(array(
'id' => '',
'class' => 'toc',
'title' => '目次',
'showcount' => 2,
'depth' => 0,
'toplevel' => 1,
'targetclass' => 'article-main'
),$atts);
$content = get_the_content();
$headers = array();
$html = '';
$toc_list = '';
$id = $atts['id'];
$toggle = '';
$counter = 0;
$counters = array(0,0,0,0,0,0);
$top_level = intval($atts['toplevel']);
$harray = array();
$targetclass = trim($atts['targetclass']);
if($targetclass===''){$targetclass = get_post_type();}
for($h = $atts['toplevel']; $h <= 6; $h++){$harray[] = '"h' . $h . '"';}
$harray = implode(',',$harray);
preg_match_all('/<([hH][1-6]).*?>(.*?)<\/[hH][1-6].*?>/u',$content,$headers);
$header_count = count($headers[0]);
if($header_count > 0){
$level = strtolower($headers[1][0]);
if($top_level < $level){$top_level = $level;}
}
if($top_level < 1){$top_level = 1;}
if($top_level > 6){$top_level = 6;}
$atts['toplevel'] = $top_level;
$current_depth = $top_level - 1;
$prev_depth = $top_level - 1;
$max_depth = (($atts['depth'] == 0) ? 6 : intval($atts['depth'])) - $top_level + 1;
for($i=0;$i < $header_count;$i++){
$depth = 0;
switch(strtolower($headers[1][$i])){
case 'h1': $depth = 1 - $top_level + 1; break;
case 'h2': $depth = 2 - $top_level + 1; break;
case 'h3': $depth = 3 - $top_level + 1; break;
case 'h4': $depth = 4 - $top_level + 1; break;
case 'h5': $depth = 5 - $top_level + 1; break;
case 'h6': $depth = 6 - $top_level + 1; break;
}
if($depth >= 1 && $depth <= $max_depth){
if($current_depth == $depth){$toc_list .= '</li>';}
while($current_depth > $depth){
$toc_list .= '</li></ol>';
$current_depth--;
$counters[$current_depth] = 0;
}
if($current_depth != $prev_depth){$toc_list .= '</li>';}
if($current_depth < $depth){
$toc_list .= '<ol' . (($current_depth == $top_level - 1) ? ' class="toc-list open"' : '') . '>';
$current_depth++;
}
$counters[$current_depth - 1] ++;
$counter++;
$toc_list .= '<li><a href="#toc' . $counter . '" tabindex="0">' . $headers[2][$i] . '</a>';
$prev_depth = $depth;
}
}
while($current_depth >= 1 ){
$toc_list .= '</li></ol>';
$current_depth--;
}
if($counter >= $atts['showcount']){
if($id!==''){$id = ' id="' . $id . '"';}else{$id = '';}
$html .= '
<aside' . $id . ' class="' . $atts['class'] . '">
<h2 class="toc-title">' . $atts['title'] . '</h2>
' . $toc_list .'
</aside>
<script>
window.onload = function () {
var idCounter = 0;
var sub = [' . $harray . '];
var targetClasses = document.getElementsByClassName("' . $targetclass . '");
for (var i = 0; i < targetClasses.length; i++) {
var targetClass = targetClasses[i];
for (var m = 0; m < sub.length; m++) {
var targetHx = String(sub[m]);
var targetElements = targetClass.getElementsByTagName(targetHx);
for (var n = 0; n < targetElements.length; n++) {
var targetElement = targetElements[n];
if (targetElement.hasAttribute("class") === false) {
idCounter++;
targetElement.id = "toc" + idCounter;
}
}
}
}
};
</script>';
}
return $html;
}
add_shortcode('toc','make_toc');
function wkwkrnht_add_quicktags(){
if(wp_script_is('quicktags')===true){ ?>
<script>
QTags.addButton('qt-toc','目次','[toc id= class=toc title=目次 showcount=2 depth=0 toplevel=1 targetclass=article-main offset=]');
</script>
<?php }
}
add_action('admin_print_footer_scripts','wkwkrnht_add_quicktags');
本当に、目次を作って表示するだけです。そのほか、PHPで処理する部分は、ほぼそのままです。それでも、記事内で、目次内のリンクとhタグをリンクさせる部分は、完全オリジナルコードです。元ネタは、jQueryを使っていますが、移植する際に、内蔵のもので動くか心配だったため、ネイティブスクリプトに書き直しました。やっていることは単純です。ショートコードから来た変数を元に、要素を同定し、その中のhタグを昇順に取得、そして、その中でクラスを持っていないものに、目次用IDを付与しています。
WordPressの最新版で動作を確認しているので、安心してください。開閉ボタンは、暇を見つけて移植したいと思います。それでも、移動時間の設定はiQueryの特権に近いので、ご自身での移植をお願いします。