当サイトでは、過去記事の年別・月別アーカイブ表示に、ウィジット「アーカイブ」ではなく、wp_get_archives()関数を使っています。

▼ wp_get_archives()関数については、こちら。
【WP】wp_get_archives()関数のパラメータと出力されるHTML
ちょうど自分でカスタマイズをしているときに、頭の整理も兼ねて、まとめたものです。

自前で関数を使ってカスタマイズしたのは、

  • アコーディオン表示(トグル表示)にして、見た目をすっきりさせたい
  • リストのアイコンを、開いているとき/閉じているときで、変えたい

と思ったことがきっかけです。

デフォルトのウィジット「アーカイブ」ではそのようなカスタマイズをしにくいので、wp_get_archives()関数を使うことにしました。

ネット上に色々な例があがっていますが、細かいところで違いもあるので、当サイトではこのようにしていますという内容をまとめてみました。

以下のプログラムから、このような挙動のアーカイブ表示になります
WordPressのアーカイブのアコーディオン表示の挙動

2020年9月にWordPressからHugoに切り替えました。そのため、現環境ではアーカイブのアコーディオン表示は行っておりません。参考までに情報を残しております。(WordPress 4.7.X)

目次

ウィジット「アーカイブ」では、表示が間延びしてしまう問題

デフォルト表示は、長い

当サイトには、2013年から約6年分の記事があるのですが、それをWordPressデフォルトで用意されているウィジット「アーカイブ」で表示しようとすると、、、

▼ WordPressデフォルトのウィジット「アーカイブ」
WordPressのウィジット「アーカイブ」の設定画面

こうなります。ひたすら、長い…。

WordPressのウィジット「アーカイブ」をそのまま使ったときの表示例

またCSSやjQueryで表示を制御しようと思っても、出力されるHTMLコードには、classやidが付かないので、あまり融通がききません。

<!-- WordPressのウィジット「アーカイブ」で出力されるHTMLの例 -->
<div id="archives-2" class="widget widget_archive">
<h4 class="widget-title">月別アーカイブ</h4>
<ul>
    <li><a href="https://example.com/2018/02/">2018年2月</a> (1)</li>
    <li><a href="https://example.com/2018/01/">2018年1月</a> (2)</li>
    <li><a href="https://example.com/2017/12/">2017年12月</a> (3)</li>
    <li><a href="https://example.com/2017/11/">2017年11月</a> (5)</li>
    <li><a href="https://example.com/2017/10/">2017年10月</a> (7)</li>
    <li><a href="https://example.com/2017/09/">2017年9月</a> (8)</li>
    <li><!-- 過去記事がある限り、以下同様にズラズラ〜っと --></li>
</ul>
</div>

ドロップダウン表示も、やはり長い

設定画面で「ドロップダウン表示」にチェックを入れることで、見た目をすっきりさせることもできますが、

▼ ドロップダウン表示例
WordPressのウィジット「アーカイブ」でドロップダウン表示にチェックを入れた場合の表示例

ローカル環境で試していたものですが、上図のセレクトボックス(select要素)は、CSSで少しオシャレに(?)、カスタマイズしています。
CSSのappearanceを使ったセレクトボックスのカスタマイズ | design Edge

クリックすると、やはり下へズラズラ〜とのびてしまうので、決して見やすくはありません。

WordPressのウィジット「アーカイブ」のドロップダウンを表示させたとき

完成イメージ

そこで、

  • 年ごとに月別アーカイブリストをまとめる
  • デフォルトでは、現在の年の月別アーカイブリストのみが表示されている状態にする
  • それ以外の年の月別リストは折りたたまれており、クリックすることで表示されるようにする
  • そして、それと同時に、すでに表示されていた年の月別アーカイブリストは折り畳まれるようにする

という状態をゴールに、wp_get_archives()関数を使ってカスタマイズを行いました。

▼ 完成図
wp_get_archives()を使った「アーカイブ」表示のカスタマイズの完成イメージ

実装

ここも色々なやり方がありますが、当サイトでは、各年別のアーカイブリストの中に、月別のアーカイブリストを入れ子にするやり方にしました。

目指すHTMLの構造は、以下の通りです。

<!-- 目指すHTMLのイメージ -->
<div class="widget">
<h4 class="monthly-archive-title">月別アーカイブ</h4>

<!-- wp_get_archives()関数を使って、実装していく部分 -->
<!-- ここから -->
<ul class="archive-list">
  <li class="year current acv_open">2018
    <ul class="month-archive-list">
      <li><a href="https://cosybench.local/2018/02/">2018年2月</a> (1)</li>
      <li><a href="https://cosybench.local/2018/01/">2018年1月</a> (2)</li>
    </ul>
  </li>
  <li class="year">2017
    <ul class="month-archive-list">
      <li><a href="https://cosybench.local/2017/12/">2017年12月</a> (3)</li>
      <li><a href="https://cosybench.local/2017/11/">2017年11月</a> (5)</li>
      <li><a href="https://cosybench.local/2017/10/">2017年10月</a> (7)</li>
      <li><a href="https://cosybench.local/2017/09/">2017年9月</a> (8)</li>
      <li><a href="https://cosybench.local/2017/08/">2017年8月</a> (8)</li>
      <!-- 以下、月ごとに同様 -->
    </ul>
  </li>
  <!-- 以下、年ごとに同様 -->
</ul>
<!-- ここまで -->

</div>

まずは、アーカイブリストすべてを、archive-listというclass名のリスト(ul)で囲んでいます。

そして、2018年、2017年、2016年などの各年が、そのリスト(archive-list)のリスト項目(li要素)になっており、それぞれyearというclass名を持っています。

さらに、各年のリスト要素は「month-archive-list」というclass名を与えられたリスト(ul)を持っており、そのリスト(month-archive-list)のリスト項目(li要素)が、1月、2月、3月などの各月になっています。

▼ 図解すると、こういう構造です

WordPressのアーカイブ表示のカスタマイズ:HTMLのリスト構造

あとは、リスト(archive-list)の開閉状態に応じてリスト項目のアイコンを変えるためにacv_openというclass名と、初期状態で現在の年のリストのみ表示させるためのcurrentというclass名を用意しました。

▼ 先頭のアイコンを、リストの開閉状態によって変えたい

WordPressのアーカイブ、トグル表示によってアイコンを変えたときの例

それでは、実際に書いていったコードです。

テンプレートファイル

このサイトでは、フッター部分で年月別アーカイブリストを表示しているので、フッターのテンプレートファイル(footer.php)に、以下のようなコードを書きました。

<!-- footer.phpに記述したコード -->
<!-- Monthly Archive -->
<div class="widget">
    <h4 class="monthly-archive-title">月別アーカイブ</h4>

    <?php
        /*== 解説(1) 参照 ==*/
        // 月別アーカイブリストを取得する
        $monthly_archives = wp_get_archives(
            $args = array(
                'type' => 'monthly',
                'show_post_count' => true,
                'before' => '',
                'after' => ',',
                'echo' => 0,
            ));
        $monthly_archives = explode(',', $monthly_archives); //配列化
        array_pop($monthly_archives); //末尾の空白要素を削除

        // 年別アーカイブリストを取得する
        $yearly_archives = wp_get_archives(
            $args = array(
                'type' => 'yearly',
                'format' => 'custom',
                'before' => '',
                'after' => ',',
                'echo' => 0,
            ));
        $yearly_archives = explode(',', $yearly_archives); //配列化
        array_pop($yearly_archives); //末尾の空白要素を削除

        /*== 解説(2) 参照 ==*/
        $this_year = (string)idate('Y'); //現在の年を、4桁の文字列で取得

        // HTMLとして出力するコードの記述部分
        // ここから
        $out = '<ul class="archive-list">';

        foreach ($yearly_archives as $year) {
            $the_year = substr($year,-8,4); // 「年」を表す部分のみ抽出する

            if ($the_year === $this_year): // 今年だったら
                $out .= '<li class="year acv_open current">' . $the_year;
                $out .= '<ul class="month-archive-list">';
            else: // それ以外の年の場合
                $out .= '<li class="year">' . $the_year;
                $out .= '<ul class="month-archive-list">';
            endif;

                /*== 解説(3) 参照 ==*/
                foreach ($monthly_archives as $month) {
                    //月毎アーカイブの文字列中に、ターゲットとなる年が含まれているか
                    $pos = strpos($month, $the_year);

                    // 含まれている限り、li要素を出力
                    if ($pos !== false): // 該当する文字列が含まれているときは、その位置が返ってくるので、!==falseという判定文を使用
                        $out .= $month;
                    endif;
                }
            $out .= '</ul>'; // 閉じる <ul class="month-archive-list">
        }
        $out .= '</li>'; // 閉じる <li class="year"> (ここ落としてしまっていたので追記しました!: 2020/03/01)
        $out .= '</ul>'; // 閉じる <ul class="archive-list">
        // ここまで

        // HTMLの出力
        echo $out;
    ?>

</div>
<!-- /Monthly Archive -->

以下、コード中にコメント表示した「/*== 解説(1)〜(3) ==*/」部分に分けて、内容をざっと解説します。

▼ シンタックスハイライター「highlight.js」の使い方
【WP】highlight.js の使い方: 必要ページだけで読み込むカスタマイズ付

コードの解説(1)

まずは、変数$yearly_archives$monthly_archivesに、wp_get_archives()関数を使って取得した年別・月別のアーカイブリストを代入しています。

そして、PHP関数のexplode()array_pop()を使い、後述するforeach部分で使いやすくなるように配列化させています。

この部分については、【WP】wp_get_archives()関数のパラメータと出力されるHTML 記事中の、【引数を ‘echo’ => ‘0’(または、false)にした場合】という項目でも、まとめています。

コードの解説(2)

これ以降は、変数$outに、出力したいHTMLをどんどん結合させて代入(.=)しています。

まず最初は、foreachで、各年のアーカイブリンクを出力させています。そして、PHPのidate()関数を使って現在の年を取得しておき、一致する場合のみ、acv_opencurrentというclassを付けています。

今年かどうか? の判定には、foreachで回している$yearに、たとえば、

<!-- $yearの中身の例 -->
<a href="http://example.com/2017/">2017</a>

といった文字列が入ることから、PHPのsubstr()関数を使って、$yearから年(4桁)を示す部分を抜き出しています。(後ろから数えて、半角8文字目から4文字分を取得)

▼ 文字列演算子について
PHP: 文字列演算子 – Manual

▼ foreach構文
PHP: foreach – Manual

▼ idate()関数
PHP: idate – Manual

▼ substr()関数
PHP: foreach – Manual

コードの解説(3)

つづいて、各年ごとに、月別のアーカイブリストを出力させています。

ここで、変数$monthly_archivesには、配列要素として、以下のような月別アーカイブリンクのリスト項目が、年に関係なく、すべて格納されています。$monthly_archive、‘format’=>‘custom’を指定していないので、各月のリンクが<li>タグでくくられます)

<!-- $monthly_archivesの中身 -->
array(n) {
[0] ="<li><a href="https://example.com/2018/02/">2018年2月</a></li>"
[1] ="<li><a href="https://example.com/2018/01/">2018年1月</a></li>"
[2] ="<li><a href="https://example.com/2017/12/">2017年12月</a></li>"
[3] ="<li><a href="https://example.com/2017/11/">2017年11月</a></li>"
<!-- 以下、略 -->
}

そこで、foreachで回していくときに、$monthが「年別リストの[年]に該当する文字列を持っているか?」との判定をかませることで、ある年に属している月別のアーカイブリンク(リスト項目)だけをスマートに! 取り出しています。(2018年であれば、2018年1月、2月のみ。2017年であれば、2017年1月〜12月までなど)

最後は、echo $out;で、カスタマイズしたHTMLを出力させ、終了です。

▼ strpos()関数について
PHP: strpos – Manual

CSSファイル

あとは、style.cssで表示スタイルを調整します。

/* acv_openクラスをもたないリスト項目の子リスト(month-archive-list)は、デフォルト非表示に */
.archive-list > li:not(.acv_open) > ul {
    display: none;
}

/* 年別アーカイブリストのアイコン設定 */
.archive-list > li.acv_open::before { /* リストが開かれているとき */
    font-family: 'fontello';
    content: '\e815'; /* -マーク */
    margin-right: .5em;
}

.archive-list > li:not(.acv_open)::before { /* リストが閉じられているとき */
    font-family: 'fontello';
    content: '\e814'; /* +マーク */
    margin-right: .5em;
    }

/* 月別アーカイブリストのアイコン設定 */
.month-archive-list > li::before {
    font-family: 'fontello';
    content: '\e802'; /* > マーク */
    margin-right: .5em;
    margin-left: 3px;
    color: rgba(0,0,0,.54);
}

メインは、yearクラスをもつ年別リスト項目の内、

  • acv_openクラスをもつリスト項目は「-」アイコンに
  • それ以外の、子リストが非表示となっているリスト項目は「+」アイコンに

とする設定です。

リスト項目のアイコンはすべて、::before疑似要素(CSS2の:before表記でも可)で指定しています。 marginなどは、適宜サイトに合わせたものに読み換えてください。また配色は、Googleが提唱している「Material Design」で推奨されているものを使用しています。

リスト要素のマークには、fontelloのアイコンフォントを使っています。
「fontello」でアイコンフォントを使ってみよう!|プラカンブログ | WEB制作会社プラスデザインカンパニー
fontelloは、「FontAwesome」など何種類かあるアイコンフォントから、使いたいものだけを選んで利用できるサービスです。

▼ Material Designについて
マテリアルデザインのガイドライン – 日本語

▼ 当サイトで使用しているカラーリングなどは、こちらに記載
【WP】オリジナルテーマ作成に欠かせなかった便利ツール集

jQuery

最後に、jQuery(JavaScript)で表示の仕方を制御します。

li.yearがクリックされたら、入れ子のul.month-archive-listを、

  • すでに開かれているときは、閉じる
  • 閉じられているときは開き、その他のすでに開かれていたul.month-archive-listは閉じる

という挙動になるようにします。

jQuery( document ).ready( function( $ ) {
  // @link: http://kachibito.net/snippets/basic-panel
  //年月別アーカイブ表示用
  $(".archive-list &gt; li").click(function() {
    //@link: https://stackoverflow.com/questions/6656202/jquery-slidedown-child-ul
    if($(this).next(".month-archive-list").is(":visible") || $(this).hasClass("acv_open")){ //既に開いている場所なら
        $(".month-archive-list", this).slideUp("fast"); //閉じる
        $(this).removeClass("acv_open"); //.acv_open削除

    }
    else { //閉じている場所なら
        $(this).siblings().children(".month-archive-list").slideUp("fast"); //その他のリストを閉じる
        $(".month-archive-list", this).slideDown("fast"); //開く
        $(".year").removeClass("acv_open"); //.acv_open削除
        $(this).addClass("acv_open"); //.acv_open付加
    }
  });
});

jQueryはネット上で得た知識の切り貼りで書いていますが、以下の2つのリンクを特に参考にさせていただきました。
特に、入れ子になっているリストの制御をどのようにすればよいかについては、2つ目リンクにあったQ&Aコーナーの回答に助けられました。

また、WordPressでは、jQuery(document).ready(function($) {という書き出しでjQuery(JavaScript)のコードを記述しますが、「なぜそのように記述しておく必要があるのか?」については、下記の解説がわかりやすかったです。

▼ ページ中ほどの【解説 – 「$」はただの記号ではない】という項から
WordPressでのダメなjQuery関数の囲み方4パターンと対策講座 | スタッフブログ | 株式会社クーネルワーク – 新潟

最後に、このjQueryのコードは、別途「hoge.js」など任意の名前を付けたファイルに保存し、WordPress関数のwp_enqueue_script()をfunctions.phpに記述することで、読み込ませています。

もちろん一般的(?)なHTMLサイトのように、<head>内や</body>直前に、ファイルへのリンクを指定して読み込ませてもよいのですが、WordPressに用意されている上記の関数を使うと、フッターが読み込まれる前に任意のタイミングでjQueryを読み込む、などの指定が簡単にできるので便利です。

追記:2018/08/05

wp_enqueue_script()は、こちらの記事でも使っています。使い方などについて、こちらの方が具体的なコードを書いて紹介しているので、イメージをつかみやすいかもしれないです。よろしければご覧ください。

追記:2022/07/23

WordPressのアップデートにより、プログラムに不具合が出てきたようです。

コメント欄にも書いていますが、PHP, JQuery, functions.phpのプログラムをアップデートしました。プログラムの構造は同じですが、アップデートに対応して改訂したものです。

ローカル環境(WordPress 6.0.1、PHP 7.4.1)でしか動作を確認しておりませんが、よろしければ参考にお使いください。

まとめ

以上になります。

やっていることは結構単純なのですが、コードと一緒に流れをまとめてみたら、なかなかボリュームのある記事になってしまいました。

そして、この記事を書いている間に、思うように動作させられていなかったjQueryのslideUp()、slideDown()周りのコードも、解決できました。客観的に振り返るって、バグ取りには大事ですね!