WordPress: シンプルなページ内目次



概要:プラグインを使わない軽量なページ内目次

*ListFreakのリストには目次は不要ですが、メンテナンス用のマニュアルに目次が欲しくなったので作りました。

特徴1:構造的でない文書にも対応

たとえば

見出しレベル2の次に見出しレベル4が来たり、

その次がレベル3だったりしても、

大丈夫。

特長2:文書中のレベルに合わせたインデント

<h1>が使われておらず <h2>が最高位であれば、<h2>がインデント無し 、<h3>が1文字下げ、<h4>が2文字下げになります。

ソースコード

add_shortcode( 'kh_toc', function() {

    // ヘッダ行を取り出す
    if ( ! preg_match_all(
        '|<h(\d)(.*)>(.*)</h\1>|iU', // Caseless, Ungreedy
        get_the_content(),
        $headers,
        PREG_SET_ORDER
    ) ) {
        return '';
    }

    // ヘッダの最高レベルを取得
    // レベル部分のマッチング結果を取り出して最小値を求める
    $highest_level = min( array_column( $headers, 1 ) );

    // $h[0]=全体、$h[1]=ヘッダレベル、$h[2]=属性、$h[3]=見出し
    $out = '<ul style="list-style: none;"><li><strong>目次</strong></li>';
    foreach ( $headers as $h ) {

        // 属性にidがあれば見出しにリンクを付与(前提:id="a1" 形式)
        if ( preg_match( '|id="(.+?)"|', $h[2], $anchor ) ) {
            $h[3] = '<a href="#'.$anchor[1].'">'.$h[3].'</a>';
        }

        // 最高レベルの次から' 'を付して目次行を作成
        //(ex. h2が最高レベルならh3は' '、h4は'  ')
        $out .= '<li>'.str_repeat( ' ', $h[1] - $highest_level ).$h[3].'</li>';
    }
    return $out.'</ul><hr>';
}

ソースコード解説

下記のような見出しが含まれた投稿があったとして、

<h2 id="id1" class="class1">見出し1</h2>
...
<h3>見出し2</h3>
...
<h2 id="id3" class="class1">見出し3</h2>
...

下記の正規表現で見出し行を取得し、さらに必要な要素に切り分けると、

'|<h(\d)(.*)>(.*)</h\1>|iU'

下記のような結果が得られる。

Array (
    [0] => Array (
            [0] => <h2 id="id1" class="class1">見出し1</h2>
            [1] => 2
            [2] =>  id="id1" class="class1"
            [3] => 見出し1
    )
    [1] => Array (
            [0] => <h3>見出し2</h3>
            [1] => 3
            [2] => 
            [3] => 見出し2
    )
    [2] => Array (
            [0] => <h2 id="id3" class="class1">見出し3</h2>
            [1] => 2
            [2] =>  id="id3" class="class1"
            [3] => 見出し3
    )
)

タグ

(Page info for admin)