LoginSignup
1
2

More than 5 years have passed since last update.

プラグインなしで、wordpressに目次を表示するショートコードを実装する方法

Last updated at Posted at 2016-12-21

私のブログには、こういうデータベース系の記事が入り込んでいて、目次があった方がいいなと思っていました。そこで、テーマ側に移植してみました。(元ネタ

以下、コードです。機能自体は、グレードダウンしています。

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の特権に近いので、ご自身での移植をお願いします。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2