【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

ご質問等ありましたら,お手数ですが弊社の個人情報保護方針をお読み頂いた上でフォームからお願い致します.

 
   
Chrome 拡張機能 PageSpeed Insights SEO WordPress Youtube お問い合わせフォーム アナリティクス アンケートフォーム サーチコンソール セミナー タグマネージャー データベース ヒアリング プラグイン ページ閲覧解析 マーケティング メール配信 リッチリザルト レスポンシブ対応 ログ解析 勉強会 商品企画 営業 営業力 営業支援システム 実装 検索順位 検索順位チェックツール 追跡 開発者ツール
contact
Pagetop