Web制作実務役立ち

WordPressのカテゴリー毎記事一覧のタブ切り替え実装方法

本ページにはプロモーションが含まれています。

WordPressのメディア型サイトを作る際、カテゴリー毎の記事一覧をタブ切り替えで表示する実装に頻繁に出くわします。

そこで今回は、非同期でカテゴリーを切り替えられる実装をご紹介します。カテゴリータブ機能は使いまわしできると、一気に自分の時給をあげられる美味しいポイントです。

コードの解説も入れているので、自分用のNotionコードスニペットにぜひ溜めてもらえればと思います。

しょーご

この記事を書いたのは
しょーご@samurabrass

このブログ「しょーごログ」の運営者。2018年からエンジニアとしてサイト制作やシステム開発を行いつつ、ブログとYouTubeで情報発信を行っている。駆け出しエンジニアのコーディング課題添削も行う。

WordPressカテゴリータブの完成見本

今回実装するカテゴリータブ切り替えの見本が以下になります。

まず「新着記事」のタブは、新規記事をカテゴリー関係なく全記事出力します。

WordPressのカテゴリー毎の記事一覧のタブ切り替え実装方法

その横にカテゴリー毎のタブが並んでいて、クリックするとカテゴリー内の記事一覧がそれぞれ出力されます。

WordPressのカテゴリー毎の記事一覧のタブ切り替え実装方法

またタブ切り替え時に非同期で切り替わるようになっています。

基本的にトップページで使用するのを前提にしているため、固定ページではコードが変わる可能性があります。

カテゴリータブの実装方法解説

全体概要

今回触るファイルは以下のみです。

  • functions.php
  • index.php(一覧を出したい任意のファイルでOK)
  • css
  • js

その他のファイルについてはそれぞれの環境があるかと思いますので、特に触れません。

functions.php

functions.phpの責務は以下です。

  • ショートコード[category_tabs]の定義と実装
  • 記事一覧を取得してHTML形式で出力する関数get_posts_list()の定義
  • カテゴリータブとそれに対応する記事一覧を生成する機能の提供

つまり、HTML含め記事一覧の出力を司る大元が、このfunctions.phpに記載されています。

以下のコードをコピペしてください。

// ショートコード関数
function category_tabs_shortcode($atts) {
  $atts = shortcode_atts(array(
      'posts_per_page' => 8,
  ), $atts);

  $categories = get_categories(array('hide_empty' => 1));
  $output = '<div class="category-tabs">';
  $output .= '<ul class="tab-nav">';
  
  // 「新着記事」タブ
  $output .= '<li class="active"><a href="#tab-recent">新着記事</a></li>';
  
  // 各カテゴリータブ
  foreach ($categories as $category) {
      $output .= '<li><a href="#tab-' . $category->term_id . '">' . $category->name . '</a></li>';
  }
  
  $output .= '</ul>';
  $output .= '<div class="tab-content">';
  
  // 新着記事の一覧(全記事を表示)
  $output .= '<div id="tab-recent" class="tab-pane active">';
  $output .= get_posts_list(array('posts_per_page' => -1, 'post_type' => 'post'));
  $output .= '</div>';
  
  // 各カテゴリーの記事一覧
  foreach ($categories as $category) {
      $output .= '<div id="tab-' . $category->term_id . '" class="tab-pane">';
      $output .= get_posts_list(array('cat' => $category->term_id, 'posts_per_page' => $atts['posts_per_page']));
      $output .= '</div>';
  }
  
  $output .= '</div></div>';
  return $output;
}
add_shortcode('category_tabs', 'category_tabs_shortcode');

function get_posts_list($args) {
  $query = new WP_Query($args);
  $output = '';
  $count = 0;
  
  if ($query->have_posts()) {
      $output .= '<div class="post-cards">';
      while ($query->have_posts() && $count < 8) {
          $query->the_post();
          $output .= '<div class="post-card">';
          if (has_post_thumbnail()) {
              $output .= '<div class="post-thumbnail">' . get_the_post_thumbnail(null, 'medium') . '</div>';
          } else {
              $output .= '<div class="post-thumbnail"><img src="' . get_template_directory_uri() . '/images/no-image.png" alt="No Image Available"></div>';
          }
          $output .= '<div class="post-content">';
          $output .= '<h2 class="post-title"><a href="' . get_permalink() . '">' . get_the_title() . '</a></h2>';
          $output .= '<div class="post-excerpt">' . wp_trim_words(get_the_excerpt(), 20, '...') . '</div>';
          $output .= '</div>'; // .post-content
          $output .= '</div>'; // .post-card
          $count++;
      }
      $output .= '</div>'; // .post-cards
  } else {
      $output .= '<p>記事が見つかりません。</p>';
  }
  
  wp_reset_postdata();
  return $output;
}

コードを塊ごとに解説していきます。

ショートコード関数 category_tabs_shortcode

この関数は、カテゴリータブ全体の構造を生成しています。

  • ショートコードの属性を設定(デフォルトで1ページあたり8投稿)
  • カテゴリーのリストを取得
  • タブのナビゲーション(ul要素)を生成
    • 「新着記事」タブを追加
    • 各カテゴリーのタブを追加
  • タブのコンテンツ(div要素)を生成
    • 「新着記事」タブの内容を追加(全投稿を表示)
    • 各カテゴリータブの内容を追加(カテゴリーごとに投稿を表示)

投稿リスト生成関数 get_posts_list

この関数は、各タブ内の投稿リストを生成しています。

  • WP_Queryを使用して投稿を取得
  • 最大8件の投稿をループで処理
    • 各投稿に対して:
      • サムネイル画像(ない場合はno-image)を表示
      • 投稿タイトルを表示
      • 投稿の抜粋(最大20単語)を表示
  • 投稿がない場合はメッセージを表示

index.php

index.phpでやっていることは以下です。

  • ブログのメインページのレイアウトと構造の定義
  • [category_tabs]ショートコードの呼び出しによるカテゴリータブの表示
  • 投稿ループの実行と各投稿の表示

基本的にfunctions.phpで定義したショートコードを呼び出しているのみです。

<div id="primary" class="content-area">
    <main id="main" class="site-main">

        <?php
        if ( have_posts() ) :
                ?>
                <header>
                    <h1 class="page-title screen-reader-text"><?php single_post_title(); ?></h1>
                </header>
                <?php

            // カテゴリータブを表示
            echo do_shortcode('[category_tabs posts_per_page="8"]');

            /* Start the Loop */
            while ( have_posts() ) :
                the_post();

                /*
                 * Include the Post-Type-specific template for the content.
                 * If you want to override this in a child theme, then include a file
                 * called content-___.php (where ___ is the Post Type name) and that will be used instead.
                 */
                get_template_part( 'template-parts/content', get_post_type() );

            endwhile;

            the_posts_navigation();

        else :

            get_template_part( 'template-parts/content', 'none' );

        endif;
        ?>

    </main><!-- #main -->
</div><!-- #primary -->

カテゴリータブの表示

echo do_shortcode('[category_tabs posts_per_page="8"]');

カスタムのカテゴリータブショートコードを実行し、結果を表示します。1ページあたり8投稿を表示するよう設定しています。

投稿が無い場合の処理

get_template_part( 'template-parts/content', 'none' );

表示する投稿が無い場合、「コンテンツなし」用のテンプレートパーツを読み込みます(別途作成してください)

CSS

cssは特に解説することはありませんが、タイトルが二行以上にならないように配慮しています。

.category-tabs {
  margin-bottom: 20px;
}

.category-tabs .tab-nav {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  border-bottom: 1px solid #ccc;
  flex-wrap: wrap;
}

.category-tabs .tab-nav li {
  margin-right: 10px;
  margin-bottom: -1px;
}

.category-tabs .tab-nav a {
  display: block;
  padding: 10px 15px;
  text-decoration: none;
  color: #333;
  background-color: #f5f5f5;
  border: 1px solid #ccc;
  border-bottom: none;
}

.category-tabs .tab-nav li.active a {
  background-color: #fff;
  border-bottom-color: #fff;
}

.category-tabs .tab-content {
  border: 1px solid #ccc;
  border-top: none;
  padding: 15px;
}

.category-tabs .tab-pane {
  display: none;
}

.category-tabs .tab-pane.active {
  display: block;
}
.post-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 20px;
  padding: 20px;
}

.post-card {
  border: 1px solid #ddd;
  border-radius: 5px;
  overflow: hidden;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  transition: transform 0.3s ease;
  display: flex;
  flex-direction: column;
}

.post-thumbnail {
  height: 200px;
  overflow: hidden;
}

.post-thumbnail img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.post-content {
  padding: 15px;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
}

.post-title {
  margin: 0 0 10px;
  font-size: 1.2em;
  line-height: 1.3;
  max-height: 2.6em; /* 2行分の高さ */
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.post-title a {
  color: #333;
  text-decoration: none;
}

.post-title a:hover {
  color: #0066cc;
}

.post-excerpt {
  font-size: 0.9em;
  color: #666;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  flex-grow: 1;
}

JS(jQuery)

JSの責務は以下になります。

  • カテゴリータブの動的な切り替え機能の実装
  • ユーザーがタブをクリックしたときの挙動の定義
  • アクティブなタブとそれに対応するコンテンツの表示制御
jQuery(document).ready(function($) {
  $('.category-tabs .tab-nav a').on('click', function(e) {
      e.preventDefault();
      var target = $(this).attr('href');
      
      $(this).parent().addClass('active').siblings().removeClass('active');
      $(target).addClass('active').siblings().removeClass('active');
  });
});

クリックされたリンクの href 属性の値を取得

var target = $(this).attr('href');

activeクラスの追加と削除

$(this).parent().addClass('active').siblings().removeClass('active');
$(target).addClass('active').siblings().removeClass('active');

activeクラスの定義はCSSで確認しておいてください。

記事カード内にカテゴリーやタグを入れる場合

記事カードの中に、カテゴリーやタグを出力する場合があるかと思います。

その時は以下のコードを入れてあげればOK。

// カテゴリーの表示
          $categories = get_the_category();
          if ($categories) {
              $output .= '<div class="post-categories"><strong>カテゴリー:</strong> ';
              foreach ($categories as $category) {
                  $output .= '<a href="' . get_category_link($category->term_id) . '">' . $category->name . '</a> ';
              }
              $output .= '</div>';
          }

          // タグの表示
          $tags = get_the_tags();
          if ($tags) {
              $output .= '<div class="post-tags"><strong>タグ:</strong> ';
              foreach ($tags as $tag) {
                  $output .= '<a href="' . get_tag_link($tag->term_id) . '">' . $tag->name . '</a> ';
              }
              $output .= '</div>';
          }

厳密に行うなら、タグやカテゴリー数の出力上限を定めたほうがいいかもですし、

もし記事カード全体をリンクにするなら、記事カード内にリンクはいらない気もします(カードのaタグと被るので)

そこは臨機応変に組み替えてみてください。

宣伝:実務レベルのWordPress課題出しています

コーディング課題 上級

コーディング課題上級編では、実務レベルのWordPress課題に挑戦していただきます。

  • プロのレビューあり
  • 初稿提出は24日以内(努力目標)
  • 修正点は10箇所以内(努力目標)

という目安を設けており、実案件さながらの緊張感の中で取り組むことが可能です。

自分の実力を試したい方はぜひ挑戦してみてください。

模擬案件チャレンジ開催のお知らせ

超実践編では納期厳守の模擬案件を経験し、スキル面以外にコミュニケーションも徹底レビューを受けることができます。

【超実践編】納期厳守の模擬コーディング案件で実務への自信を身につける!【学習沼からあなたを卒業させます】

最近は実案件のノウハウも多いですが、「納期が短い案件の中で、丁寧なコミュニケーションを本当に実践できますか?

この課題では、極限まで実案件に近い状況で、発注者である私とコミュニケーションを取りながら、

  1. 見積書提出
  2. 実装→初稿提出
  3. レビュー→修正
  4. 再修正→納品
  5. 請求書

この流れを実践していただき、最後にzoomであなたに全体レビューを行います。

【超実践編】納期厳守の模擬コーディング案件で実務への自信を身につける!【学習沼からあなたを卒業させます】
  • 学習はだいたい終わったけど、納期までに納品できるか不安
  • 中々継続と紹介で案件が回らない

このような中級者を飛躍させる超実践編、受講には条件がありますので、詳細はリンク先よりご確認下さい。

しょーご

あなたの挑戦を待っています!!

\レビューを受けて圧倒的な自信を身につける!/

応援して頂ける方へ

ご寄付を頂けると今後の更新の励みになります!

🍺 ビールをプレゼントする

あなたに是非読んでほしい記事です!
});