Ideal Reality

興味の赴くままに

[WordPress]記事のもくじをJavascriptで生成する

WordPressの記事には、ユーザビリティの観点からもくじがあった方がいいですよね。記事の内容をざっくりと把握できますし、見たい場所まですぐに移動できます。

もくじを作る方法は大体

  • 手作業で作る
  • functions.phpでもくじを挿入するフィルターを追加する
  • プラグインを使う

このどれかだと思うんですが、色々と思うところがあったのでJavascriptを使ってブラウザに生成させることにしました。

Contents
スポンサーリンク

なぜJavascriptなのか

手作業でもくじを作るのは面倒すぎるので論外として、プラグインなしでもくじを作る方法を調べるとfunctions.phpに追記する方法が出てきます。

で、この追記するコードが何やってるかって話なんですけど、記事の全文を正規表現でマッチさせてヘッドライン(h2など)をリストアップ&アンカーポイントのIDをセットし、その結果からリストを生成して記事の前方に挿入するってことを行ってます。おそらくもくじを生成するプラグインも同じようなことをやってるはず。これを記事が読み込まれる度、毎回やってるわけです。

そうなると気になるのがサーバーの負荷ですよね。この程度の処理なんか最近のマシンにかかれば全然余裕でしょうが、塵も積もればってことがあるのでなるべくこういう全文探査するようなものは減らしておきたい。

あと、ブラウザ側なら表示のためにDOMツリーを生成しているので、JavascriptのDOM操作が使えて正規表現使うよりも簡単にできるし。(どちらかというとこっちの理由の方が大きいかも)

そこで、Javascriptを使ってクライアントに生成させようと思ったのです。

コード

window.addEventListener('DOMContentLoaded', function() {
    var contents = document.querySelector(".post-inner"); // 記事の入った要素を取得
    if (!contents) return; // 記事がないなら終了
    var firstH2 = contents.querySelector("h2"); // 最初のh2を取得
    if (!firstH2) return; // h2が存在しないなら終了
    var indexArray = [];
    var src = "<div id='post-index-title'>Contents</div>";
    var headlines = contents.querySelectorAll('h2, h3, h4');
    Array.prototype.slice.call(headlines, 0).forEach(function(headline) {
        var level = Number(headline.tagName.charAt(1)) - 1;
        while (level > indexArray.length) {
            src += "<ul>";
            indexArray.push(0);
        }
        while (level < indexArray.length) {
            src += "</ul>";
            indexArray.pop();
        }
        indexArray[indexArray.length - 1]++;
        if (!headline.id) headline.id = "ContentsIndex_" + indexArray.join("-");
        src += "<li><a href='#" + headline.id + "'>" + headline.innerText + "</a></li>";
    });
    while (0 < indexArray.length) {
        src += "</ul>";
        indexArray.pop();
    }
    var container = document.createElement("div");
    container.id = "post-index-container";
    container.innerHTML = src;
    firstH2.parentNode.insertBefore(container, firstH2);
});

このコードをfooter.php内にscriptタグで囲って入れたり、.jsファイルを用意してテーマから読み込むなど、とにかくページが表示された際に実行されるようにしてください。

やってることは難しいこともなく、querySelectorAll('h2, h3, h4')forEachで回してヘッドラインにIDを指定しながら文字列操作でもくじのhtmlをごりごり生成しているだけ。あとはそれを最初に出現したh2タグの直前に挿入すれば終わりです。

ちなみにforEachあたり複雑な書き方になってるのはIE対策のため。

あと、document.querySelector(".post-inner")の要素内に記事が展開されてること前提で書いてますけど、記事を内包する要素につけるクラス名はテーマによって違ったりするので、ここは自身のテーマに合わせて変えてください。とりあえずTwenty Twentyのテーマをもとにサンプルコード書いてます。

Twenty Twentyで実行した結果

あとはご自由にCSSで装飾してもらえればってところですね。

スポンサーリンク

SEO的にどうなの?

正直、クライアント側のJavascriptで生成する関係上、クローラーにもくじを認識してもらうことは期待していませんでした。その程度別にいいやって思ってたんですよ。

ところが、ちゃんと検索エンジンに認識されて、検索結果にもくじの一部が表示されるようになり、しかもDOMContentLoadedでセットされたIDの要素に対してブラウザはちゃんと移動してくれました。

最近、Javascriptで内容を生成するサイトが増えたからでしょうか。クローラーがJavascriptをちゃんと解釈してくれているあたり、SEO的には問題なさそうに思えます。

問題点は?

Javascriptが無効化されたユーザーにはもくじが表示されない程度です。別に表示されなかったところで致命的な問題になることもないし、Javascriptを使ってないユーザーなんて滅多にいないだろうから気にしなくてもいいと思う。

スポンサーリンク

そもそもの話

記事の内容なんて、自分が編集しない限り時間が経って変わることなんてないのだから、記事が表示される度に毎回生成すること自体が無駄。

記事を保存する時に1度だけ自動で生成してくれればそれで済む話じゃん。

まあ既存の記事全てを一度処理しなきゃいけないって手間があるから、一般ユーザーがプラグインをインストールするだけで簡単に導入できるって手段じゃないけど。

というわけなので、今回紹介した方法は以前まではこのサイトで使用していたものですが、現在はGutenbergを無効化し、自作した独自のエディタに同じようなコードを入れて、記事を書いているブラウザ上でもくじを自動生成するようにしています。

ですが、この方法自体は使える方法だと思うので記事にしました。よければ参考にどうぞ。

スポンサーリンク

コメント

投稿されたコメントはありません

名前

メールアドレス(任意)

コメント

関連する投稿

[WordPress]メディアライブラリから画像を選べるカスタムフィールドを作成する

CentOS 8でNginxを使ったWordPressサーバーを構築する

Raspberry Pi (Raspbian)上にNginxを使ったWordPressサーバーを構築する

highlight.jsを非同期で読み込む

highlight.jsに行番号をつける

SlackでURLが展開されないのを修正する