【WordPress】目次自動作成スクリプト改良版【javascript少な目】

最終更新日: 公開日: 2022年11月

以前,「【WordPress】目次を自動生成するには」という目次自動生成スクリプトを紹介したことがあった.

今回,「A タグの href に javascript:void(0); とか入れんな」という突っ込みを PageSpeed Insights 様から受けたので対応するためにプログラムを改良した.
折角なので公開する.

WordPress の目次自動作成

WordPress の目次自動作成プログラム

WordPress で記事に自動的に目次を作成するプログラムである.
目次を一々作らなくても見出しから自動的に作ってくれるので,自分で作ることを考えるととても楽が出来る.
functions.php に入れるだけなので実装も簡単だ.

目次の「閉じる」「開く」を CSS だけで動作するように改良

元々のプログラムでは「閉じる」「開く」を javascript で実装していた.
その「閉じる」「開く」ボタンが A タグで作成されていて,href に javascript:void(0); と書かれていたのだ.
そのため,PageSpeed Insights で警告を受けた.

この部分はアコーディオン表示なので「CSS のみでアコーディオン表示する」を参考にしながら,この部分を javascript を使わないように書き替えた.

PHP と javascript のソースコード

これを functions.php に挿入すればよい.

class Toc_Shortcode {

  private $add_script = false;
  private $atts = array();

  public function __construct() {
    add_shortcode('toc', array( $this, 'shortcode_content' ) );
    add_action('wp_footer', array( $this, 'add_script' ), 999999 );
    add_filter('the_content', array( $this, 'change_content' ), 9 );
  }

  function change_content( $content ) {
    return "<div id=\"toc_content\">{$content}</div>";
  }

  public function shortcode_content( $atts ) {
    global $post;

    if ( ! isset( $post ) )
      return '';

    $this->atts = shortcode_atts( array(
      'id' => '',
      'class' => 'toc',
      'title' => '目次',
      'showcount' => 2,
      'depth' => 0,
      'toplevel' => 2,
      ), $atts );

    $content = $post->post_content;

    $headers = array();
    preg_match_all( '/<([hH][1-6]).*?>(.*?)<\/[hH][1-6].*?>/us', $content, $headers );
    $header_count = count( $headers[0] );
    $counter = 0;
    $counters = array( 0, 0, 0, 0, 0, 0 );
    $current_depth = 0;
    $prev_depth = 0;
    $top_level = intval( $this->atts['toplevel'] );
    if ( $top_level < 1 ) $top_level = 1;
    if ( $top_level > 6 ) $top_level = 6;
    $this->atts['toplevel'] = $top_level;

    // 表示する階層数
    $max_depth = ( ( $this->atts['depth'] == 0 ) ? 6 : intval( $this->atts['depth'] ) );

    $toc_list = '';
    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></ul>';
          $current_depth--;
          $counters[$current_depth] = 0;
        }
        if ( $current_depth != $prev_depth ) {
          $toc_list .= '</li>';
        }
        while ( $current_depth < $depth ) {
          $class = $current_depth == 0 ? ' class="toc-list"' : '';
          $toc_list .= "<ul{$class}>";
          $current_depth++;
        }
        $counters[$current_depth - 1]++;
        $number = $counters[0] . '.';
        for ( $j = 1; $j < $current_depth; $j++ ) {
          $number .= $counters[$j] . '.';
        }
        $counter++;
        $toc_list .= '<li><a href="#toc' . ($i + 1) . '"><span class="contentstable-number">' . $number . '</span> ' . $headers[2][$i] . '</a>';
        $prev_depth = $depth;
      }
    }
    while ( $current_depth >= 1 ) {
      $toc_list .= '</li></ul>';
      $current_depth--;
    }

    $html = <<<EOD
<style>
.toc input {
  display: none;
}
.toc {
  max-height: inherit;
  overflow-y: visible;
}
.toc .toc-toggle {
  text-align: center;
  display: block;
}
.toc .toc-list {
  max-height: inherit;
  overflow-y: visible;
  visibility: visible;
  -webkit-transition: all 0.5s;
  -moz-transition: all 0.5s;
  -ms-transition: all 0.5s;
  -o-transition: all 0.5s;
  transition: all 0.5s;
}
.toc .clickarea {
  padding: 6px;
}
.toc input[type="checkbox"]:checked ~ .toc-list {
  max-height: 0;
  overflow-y: hidden;
  visibility: hidden;
  opacity: 1;
}
.toc .clickarea:before {
  content: "[閉じる]";
}
.toc input[type="checkbox"]:checked ~ * .clickarea:before {
  content: "[開く]";
}
</style>
EOD;
    
    if ( $counter >= $this->atts['showcount'] ) {
      $this->add_script = true;

      $toggle = ' <input type="checkbox" id="tocclose"/><label for="tocclose" class="toc-toggle"><span class="toc-title">' . $this->atts['title'] . '</span><span class="clickarea"></span></label>';

      $html .= '<div' . ( $this->atts['id'] != '' ? ' id="' . $this->atts['id'] . '"' : '' ) . ' class="' . $this->atts['class'] . '">';
      $html .= $toggle . $toc_list;
      $html .= '</div>' . "\n";
    }

    return $html;
  }

  public function add_script() {
    if ( ! $this->add_script ) {
      return false;
    }
?>
<script>
let xoToc = () => {
  const entryContent = document.getElementById('toc_content');
  if (!entryContent) {
    return false;
  }

  /*
   * ヘッダータグに ID を付与
   */
  const headers = entryContent.querySelectorAll('h1, h2, h3, h4, h5, h6');
  for (let i = 0; i < headers.length; i++) {
    headers[i].setAttribute('id', 'toc' + (i + 1));
    var cls = headers[i].getAttribute('class');
    if (cls == null) { cls = ''; }
    headers[i].setAttribute('class', cls + ' anchor');
  }
};
xoToc();
</script>
<?php
  }
}
new Toc_Shortcode();

スタイルシートのコード

これは同じなので以前作った「【WordPress】目次を自動生成するには」のページを参考にどうぞ.

 

Contact

ご質問等ありましたら,お手数ですが弊社の個人情報保護方針をお読み頂いた上でフォームからお願い致します.
※このページと無関係な内容のセールスはご遠慮ください.

 
   
contact
Pagetop