べんりワザ・WordPressのカスタマイズの土台

パンくずリストをプラグインなしで作る

投稿・固定ページ・カテゴリ・カスタム投稿タイプに対応したパンくずリストをfunctions.phpで自作するコード。CSSと、検索結果に効く構造化データの付け方までまとめました。

パンくずリストは、プラグインなしで作れますfunctions.phpに関数を1つ用意して、テンプレートから呼ぶだけ。プラグインに依存しないので、テーマを配布・納品するときも安心です。

まずは完成コードから。

/**
 * パンくずリストを出力する。
 * 各項目は ['label' => 表示名, 'url' => リンク先(最後の項目はnull)]
 */
function my_breadcrumb() {
  if (is_front_page()) {
    return;   // トップでは出さない
  }

  $items = [['label' => 'ホーム', 'url' => home_url('/')]];

  if (is_home()) {
    // 投稿一覧
    $items[] = ['label' => get_the_title(get_option('page_for_posts')) ?: 'お知らせ', 'url' => null];

  } elseif (is_category() || is_tag() || is_tax()) {
    $items[] = ['label' => single_term_title('', false), 'url' => null];

  } elseif (is_post_type_archive()) {
    $items[] = ['label' => post_type_archive_title('', false), 'url' => null];

  } elseif (is_search()) {
    $items[] = ['label' => '「' . get_search_query() . '」の検索結果', 'url' => null];

  } elseif (is_404()) {
    $items[] = ['label' => 'ページが見つかりません', 'url' => null];

  } elseif (is_page()) {
    // 親ページをたどる
    foreach (array_reverse(get_post_ancestors(get_the_ID())) as $ancestor) {
      $items[] = ['label' => get_the_title($ancestor), 'url' => get_permalink($ancestor)];
    }
    $items[] = ['label' => get_the_title(), 'url' => null];

  } elseif (is_singular()) {
    $type = get_post_type();

    if ($type === 'post') {
      // 投稿はカテゴリを1つ挟む
      $cats = get_the_category();
      if ($cats) {
        $items[] = ['label' => $cats[0]->name, 'url' => get_category_link($cats[0]->term_id)];
      }
    } else {
      // カスタム投稿タイプは一覧ページを挟む
      $obj = get_post_type_object($type);
      if ($obj && $obj->has_archive) {
        $items[] = ['label' => $obj->labels->name, 'url' => get_post_type_archive_link($type)];
      }
    }
    $items[] = ['label' => get_the_title(), 'url' => null];
  }

  // 出力
  echo '<nav class="breadcrumb" aria-label="現在位置"><ol>';
  foreach ($items as $item) {
    echo '<li>';
    if ($item['url']) {
      echo '<a href="' . esc_url($item['url']) . '">' . esc_html($item['label']) . '</a>';
    } else {
      echo '<span aria-current="page">' . esc_html($item['label']) . '</span>';
    }
    echo '</li>';
  }
  echo '</ol></nav>';
}

テンプレートからは1行で呼び出します。

<?php my_breadcrumb(); ?>

コードの考え方

やっていることはシンプルです。

  1. 配列を組み立てる——「ホーム」から始めて、階層順に項目を足す
  2. 最後の項目はリンクにしない(今いるページなので)
  3. まとめて出力する

表示するHTMLと組み立てを分けているのがポイントです。olliで階層を表し、aria-labelaria-currentを付けておくと、読み上げでも位置が伝わります。

べんりワザWordPressの条件分岐タグ早見表分岐に使っているis_〇〇(条件分岐タグ)の早見表はこちら

CSSを当てる

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  font-size: 0.875rem;
  color: #666;
}
.breadcrumb li + li::before {
  content: "›";
  margin-right: 0.5em;
  color: #aaa;
}
.breadcrumb a { color: inherit; }

区切り記号はCSSの::beforeで入れるのがコツです。HTMLに>を書きこむと、読み上げで余計に読まれたりコピー時に混ざったりします。

構造化データを付ける

パンくずをGoogleに理解させると、検索結果にパンくずが表示されることがあります。JSON-LDで出力します。

function my_breadcrumb_jsonld($items) {
  $list = [];
  foreach ($items as $i => $item) {
    $entry = [
      '@type'    => 'ListItem',
      'position' => $i + 1,
      'name'     => $item['label'],
    ];
    if ($item['url']) {
      $entry['item'] = $item['url'];
    }
    $list[] = $entry;
  }

  $data = [
    '@context'        => 'https://schema.org',
    '@type'           => 'BreadcrumbList',
    'itemListElement' => $list,
  ];

  echo '<script type="application/ld+json">'
    . wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
    . '</script>';
}

上のmy_breadcrumb()の中で、HTMLを出力したあとにこの関数を呼びます。

投稿一覧ページの名前を正しく出す

「設定」→「表示設定」で投稿ページを固定ページに割り当てている場合、その固定ページのタイトルをパンくずに使うのが自然です。

$blog_id = (int) get_option('page_for_posts');
$label   = $blog_id ? get_the_title($blog_id) : 'お知らせ';

投稿の詳細ページでも、カテゴリの前にこの階層を挟むとより正確になります(サイトの構造に合わせて調整してください)。

プラグインを使う場合との比較

  • 自作——出力するHTMLを完全に決められる。プラグインが増えない。テーマに同梱できる
  • プラグイン——設定画面で調整できる。分岐の作りこみが不要

SEO系プラグインにはパンくず機能が含まれていることが多いので、すでに入れているなら重複させないのが大事です。自作するなら、プラグイン側の機能はオフにしておきます。

まとめ

  1. パンくずはfunctions.phpに関数1つで自作できる。プラグイン依存を増やさない
  2. 配列に階層を積んで、最後の項目だけリンクなし——これが実装の型
  3. olaria-currentでマークアップし、区切り記号はCSSの::beforeで入れる
  4. 構造化データ(BreadcrumbList)を付けると検索結果に出ることがある。表示内容と一致させる

今どこにいるかが分かる導線は、回遊率にも直結します。

よくある質問

パンくずリストはSEOに効果がありますか?
サイト構造を検索エンジンに伝えられるうえ、構造化データを付けると検索結果にパンくずが表示されることがあります。訪問者が今どこにいるか分かるという実利のほうが大きく、内部リンクとしても機能します。
プラグインを使わずに作れますか?
作れます。functions.phpに関数を1つ書き、テンプレートから呼び出すだけです。条件分岐タグでページの種類を判定して、種類ごとにリンクを組み立てます。
カスタム投稿タイプにも対応できますか?
できます。is_singular()で投稿タイプを判定し、get_post_type_object()から一覧ページのラベルとURLを取得して中間の階層に入れます。
構造化データはどう付けますか?
パンくずの各項目をBreadcrumbListとしてJSON-LDで出力します。表示しているパンくずと同じ内容にすることがGoogleのポリシーです。