Pages

2026/02/26

厳選Bloggerページネーション3+1:自作も加えた最強ガイド

Bloggerの標準ナビゲーション(「前の投稿」「次の投稿」)に、物足りなさを感じていませんか?

ブログにおいて、ナビゲーションは読者を迷わせないための「地図」です。数字でページが並ぶ「ページネーション」を導入することは、単に便利になるだけでなく、実は大きなメリットがあります。

  • 回遊率の向上: 「あと何ページあるか」が見えることで、読者が他の記事にも手を伸ばしやすくなります。
  • デザインの自由度: カスタムページャーを採用することで、CSSを使ってブログの雰囲気に合わせた「自分だけのデザイン」に作り替えることが可能になります。

今回は、そんなBloggerカスタマイズの必須アイテムとも言えるページネーションについて、導入の仕組みが異なる「3つの王道パターン」と、表示件数の整合性にまでこだわった「究極の+1」を厳選して解説します。

あなたのブログをより使いやすく、より自分らしく仕上げるためのヒントになれば幸いです!

IB-Note
IB-Note

導入難易度 : ★★★☆☆

導入方法|HTML画面にペースト
特徴|高速 & 軽量 / 無駄な処理が発生しない最適化設計 / ラベルページ 一覧 ◯ 対応 / 検索結果一覧ページ ◯ 対応 / アーカイブ一覧ページ × 非対応

BLOGGER LABO

導入難易度 : ★☆☆☆☆

導入方法|HTML/JavaScriptガジェットへペースト
特徴|コード準備と設置方法がシンプル / 3種類のデザイン / ラベルページ一覧 △ 一部テーマで非動作 / 検索結果一覧ページ △ 導入しただけでは非対応 / アーカイブ一覧ページ× 非対応

Bloggerテンプレート自作カスタム

導入難易度 : ★★★

導入方法|HTML編集画面でペースト
特徴|投稿フィードから総投稿数を取得し、ページリンクを計算 / 必要なフィード範囲だけを検索し最適化 / ラベルページ一覧 △ / 検索結果一覧ページ △ / アーカイブ一覧ページ × 非対応
※ あらかじめ検索結果・ラベルページの表示件数を合わせる作業が必要

matsu_Uカスタム版

カスタマイズの経緯

本カスタム版は、Blogger界隈で多くの知見を公開されている IB-Noteさんのコード をベースに、私のブログ環境に合わせて微調整を加えたものです。

私自身はWeb制作の実務経験はありませんが、ChatGPTやGeminiとの対話を通じて、ロジックを一つずつ紐解きながら形にしていきました。

AIの助けを借りて、素人なりに「納得できる整合性」を少しずつ積み上げた結果です。素晴らしい先人たちのコードと並んで、「自分に合ったページネーション選び」のひとつの選択肢にあがれば嬉しいです。

どうにかできないかと試行錯誤した「こだわり」

このカスタム版を作る上で、私が一番に考えたのは、「ブログ全体の徹底的な整合性」です。 Bloggerを使っていると、ページによって表示件数が微妙にズレたりすることがありますが、その現象を「どうにか解決できないかな……」と模索したのが始まりでした。

  • 表示件数をどこまでも揃える:トップページ、ラベル一覧、検索結果のすべてで、設定した件数がピタリと維持されるよう調整を重ねました。
  • アーカイブ一覧への挑戦:月別・年別などの「アーカイブ一覧」でも表示件数を揃えたかったのですが、自分なりに探した限りでは同様の実装例が見当たらなかったため、AIと一緒に「どうにかできないか」と知恵を絞ってロジックを組み込みました。
  • 素人目線の「なぜ?」を大切に:Web制作の実務経験がないからこそ感じた「なぜここがズレるのか?」という素朴な疑問をAI(ChatGPT/Gemini)にぶつけ、納得いくまで調整を繰り返しました。

専門的な知識がなくても、AIという相棒と向き合うことで、自分なりに満足のいく形を完成させることができました。「どこをめくっても、常に決まった件数が並ぶ」――そんな当たり前の心地よさを、ぜひ試してみてください。

matsu_U 版

導入難易度 : ★★★★★

導入方法|HTML画面でセクションを作成後、コードをペーストして設置

特徴|AIと素人がIB-Noteさんのコードをベースに改変 / ラベル一覧ページ ◯ 対応 / 検索結果一覧ページ ◯  オプションで対応  / アーカイブ一覧ページ △ 一部対応

まつさん
まつさん
導入する前に必ずテーマのバックアップを取ってください!!
お兄さんとの約束ダゾ!!

テスト・検証環境について

本コードは、以下の環境で動作確認を行っていますが、それぞれ「どこまで手を加えたか」に違いがあります。

  • ラクーンドッグ|たぬき男の話
    今回紹介している最小構成のコードで動作するはずです。
  • Materiapollo|Cottpic
    当ブログ(https://blogger.matsusanjpn.com)で実装運用中。
    ※こちらは多機能なため、テーマ独自の挙動と競合しないよう、紹介しているコードにさらに追加の調整コードを追加した状態での動作を確認しております。

シンプルな環境と、カスタム環境の両方で「整合性」が取れることを確認済みでではあります。

不具合というか現状仕様

アーカイブ一覧ページの1ページ目が指定件数の±1件の誤差が出る場合があります。2ページ目以降はおそらく問題ないはずです。

ソースコードと導入方法

本カスタム版は、ご利用の環境や「どこまで整合性を求めるか」に合わせて、以下の3つのパターンを用意しました。

選べる3つのパターン

  • 1. 最小構成版:トップページとラベル一覧のみに対応。シンプルに使いたい方向け。
  • 2. +検索一覧版:ブログ内検索の結果ページでも表示件数を制御したい方向け。
  • 3. +アーカイブ一覧版:【フルスペック】月別・年別アーカイブまで完璧に揃えたい方向け。

ご自身のブログの運用スタイルに合わせて、最適なコードを選択してください。

設置場所 ~ テンプレートに「専用セクション」を新設する

Bloggerの「テーマ」>「HTMLの編集」を開き、</main> という閉じタグを探してください。(※テンプレートによっては </article> や </div> の場合もありますが、基本はメインコンテンツの終わり際です)


<b:section id='pagination-section' class='pagination-section' showaddelement='yes'/>
  

レイアウト画面に戻ると、今作った pagination-section という新しい箱が出現しています。そこに「HTML/JavaScript」ガジェットを追加してください。

ベースコードを設置

新設したHTML/JavaScriptガジェットの中に以下のコードを挿入します。

ソースコード (3パターンすべてで必要なコードです)

HTML・CSS JavaScript

<style>
    .pagination {
      display: flex;
      justify-content: center;
    }
    .pagination-link, .pagination-ellipsis {
      display: flex;
      justify-content: center;
      width: 40px;
      height: 40px;
      align-items: center;
      font-size: 1em;
      border-radius: 50%;
      margin: 0 5px;
      text-decoration: none;
    }
    .pagination-link {
      color: #444 !important;
      background: #fff;
      box-shadow: 0 2px 4px rgb(0 0 0 / 20%), 0 1px 2px 0 rgb(0 0 0 / 20%);
    }
    .pagination-link:hover {
      text-decoration: none;
    }
    .pagination-link.current-page {
      color: #fff !important;
      background: dodgerblue;
    }
    .pagination-ellipsis {
      color: #999;
      pointer-events: none;
    }
.blog-posts article:nth-of-type(n+7),
.blog-posts .post-outer-container:nth-of-type(n+7),
.blog-posts .date-outer:nth-of-type(n+7) {
  display: none !important;
}

.blog-posts {
  display: flex !important;
  flex-direction: column !important;
}
/* --- ページネーション全体のデザイン --- */
.custom-pager {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 40px 0;
  gap: 8px; /* 数字同士の間隔 */
  font-family: 'Helvetica Neue', Arial, sans-serif;
}

/* --- 数字(リンク)のスタイル --- */
.custom-pager a, .custom-pager span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 5px;
  text-decoration: none;
  border-radius: 4px;
  transition: all 0.3s ease;
  font-size: 14px;
  box-sizing: border-box;
}

/* --- 通常の数字リンク --- */
.custom-pager a {
  background-color: #f8f9fa;
  color: #333;
  border: 1px solid #dee2e6;
}

/* --- ホバー(マウスを乗せたとき) --- */
.custom-pager a:hover {
  background-color: #e9ecef;
  border-color: #adb5bd;
  transform: translateY(-2px); /* 少し浮き上がる */
}

/* --- 現在のページ(アクティブ) --- */
.custom-pager .pager-current-active, 
.custom-pager span[style*="background:#333"] { /* スクリプト内蔵版への対応 */
  background-color: #333 !important; /* お好みの色に変えてもOK! */
  color: #fff !important;
  font-weight: bold;
  border: 1px solid #333;
  pointer-events: none; /* クリック不可 */
}

/* --- 「...」のスタイル --- */
.custom-pager .pager-dots {
  color: #adb5bd;
  border: none;
  background: none;
}

/* --- スマホ対応 --- */
@media (max-width: 480px) {
  .custom-pager a, .custom-pager span {
    min-width: 35px;
    height: 35px;
    font-size: 13px;
  }
}

/* アーカイブページで7件目以降の記事を非表示にする */
.archive-page .post-outer:nth-child(n+7) {
  display: none !important;
}

/* もし投稿のクラス名が post-outer ではない場合は、ブログの構造に合わせて調整 */
/* 例: .blog-posts > .date-outer:nth-child(n+7) { display: none !important; } */

/* アーカイブページで7件目以降の記事を物理的に非表示にする */
.archive-page .post-outer:nth-child(n+7) {
  display: none !important;
}

/* ついでに、1ページ目だけに「不自然な余白」が出るのを防ぐ */
.archive-page .blog-posts {
  display: flex;
  flex-direction: column;
}

    .pagination {
      display: flex;
      justify-content: center;
    }
    .pagination-link, .pagination-ellipsis {
      display: flex;
      justify-content: center;
      width: 40px;
      height: 40px;
      align-items: center;
      font-size: 1em;
      border-radius: 50%;
      margin: 0 5px;
      text-decoration: none;
    }
    .pagination-link {
      color: #444 !important;
      background: #fff;
      box-shadow: 0 2px 4px rgb(0 0 0 / 20%), 0 1px 2px 0 rgb(0 0 0 / 20%);
    }
    .pagination-link:hover {
      text-decoration: none;
    }
    .pagination-link.current-page {
      color: #fff !important;
      background: dodgerblue;
    }
    .pagination-ellipsis {
      color: #999;
      pointer-events: none;
    }
.blog-posts article:nth-of-type(n+7),
.blog-posts .post-outer-container:nth-of-type(n+7),
.blog-posts .date-outer:nth-of-type(n+7) {
  display: none !important;
}

.blog-posts {
  display: flex !important;
  flex-direction: column !important;
}
   
/* 親分を並び替え可能な箱にする */
#id-maisection-dayo .main {
  display: flex !important;
  flex-direction: column !important;
}

#id-maisection-dayo .main #Blog1, 
#id-maisection-dayo .main [id^=&#39;Blog&#39;] {
  order: 1 !important;
}

/* ページャー&#65288;HTML2&#65289;を2番目に&#65288;下&#65289; */
#id-maisection-dayo .main #HTML2 {
  order: 2 !important;
  margin-top: 30px; /* 記事との間に少し余裕を */
}   

  </style> 
  

<b:if cond='data:blog.pageType in {"index", "archive"}'>
  <div class='pagination'/>
  <script type='text/javascript'>
  const blogUrl = &quot;<data:blog.homepageUrl.canonical/>&quot;;
  const label = &quot;<b:eval expr='data:blog.searchLabel'/>&quot;;
  const query = &quot;<b:eval expr='data:blog.searchQuery'/>&quot;;
  const selector = &#39;.pagination&#39;;
  const maxResults = 6; 

  //<![CDATA[
  setupPagination();

  function setupPagination() {
    let currentPath = window.location.pathname;
    let base = blogUrl.replace(/\/$/, "");
    let feedUrl = base + '/feeds/posts/summary';
    
    // アーカイブ判定と年月抽出
    let archiveMatch = currentPath.match(/\/(\d{4})\/(\d{2})\//);
    let isArchive = !!archiveMatch;

    if (isArchive) {
      // GPT先生の知恵:日付範囲で攻める
      let year = archiveMatch[1];
      let month = archiveMatch[2];
      let lastDay = new Date(year, month, 0).getDate(); // 月末日を自動計算
      feedUrl += '?published-min=' + year + '-' + month + '-01T00:00:00';
      feedUrl += '&published-max=' + year + '-' + month + '-' + lastDay + 'T23:59:59';
    } else if (label) {
      feedUrl += '/-/' + encodeURIComponent(label);
    }
    
    feedUrl += (feedUrl.indexOf('?') <= -1 ? '?' : '&') + 'alt=json&max-results=1';

    if (query) {
      feedUrl = base + '/feeds/posts/summary?alt=json&max-results=99&q=' + encodeURIComponent(query);
    }

    fetch(feedUrl)
      .then(r => r.json())
      .then(json => {
        if (!json.feed || !json.feed.openSearch$totalResults) return;
        const totalResults = parseInt(json.feed.openSearch$totalResults.$t, 10);
        const totalPages = Math.ceil(totalResults / maxResults);

        let currentPage = 1;
        if (location.href.indexOf('#Page-') > -1) {
          currentPage = parseInt(location.href.split('#Page-')[1]);
        }

        if (totalResults > maxResults) {
          let pagerHtml = '';
          for (let i = 1; i <= totalPages; i++) {
            if (i === currentPage) {
              pagerHtml += '<span class="pagination-link current-page">' + i + '</span>';
            } else {
              pagerHtml += '<a class="pagination-link" id="Page-' + i + '" href="#">' + i + '</a>';
            }
          }
          const pagerElem = document.querySelector(selector);
          if (pagerElem) {
            pagerElem.innerHTML = pagerHtml;
            for (let i = 1; i <= totalPages; i++) {
              if (i === currentPage) continue;
              setPageUrl(i, isArchive, archiveMatch);
            }
          }
        }
      });
  }

  function setPageUrl(targetPage, isArchive, archiveMatch) {
    let startIndex = (targetPage - 1) * maxResults;
    if (startIndex <= 0) startIndex = 1;

    let base = blogUrl.replace(/\/$/, "");
    let feedUrl = base + "/feeds/posts/summary";
    let currentPath = window.location.pathname;

    if (isArchive) {
      let year = archiveMatch[1];
      let month = archiveMatch[2];
      let lastDay = new Date(year, month, 0).getDate();
      feedUrl += '?published-min=' + year + '-' + month + '-01T00:00:00';
      feedUrl += '&published-max=' + year + '-' + month + '-' + lastDay + 'T23:59:59';
    } else if (label != '') {
      feedUrl += '/-/' + encodeURIComponent(label);
    }
    feedUrl += (feedUrl.indexOf('?') <= -1 ? '?' : '&') + 'alt=json&orderby=published&start-index=' + startIndex + '&max-results=1';

    fetch(feedUrl)
      .then((response) => response.json())
      .then((json) => {
        if (!json.feed || !json.feed.entry) return;
        const date = encodeDate(json.feed.entry[0].published.$t);

        let hrefBase = isArchive ? (base + currentPath) : (base + '/search' + (label != '' ? '/label/' + encodeURIComponent(label) : ''));
        let finalHref = hrefBase.replace(/\/$/, "") + '?updated-max=' + date + '&max-results=' + maxResults + '&start=' + startIndex + '&by-date=false#Page-' + targetPage;

        const liElem = document.querySelector(selector + ' #Page-' + targetPage);
        if (liElem) liElem.href = finalHref;
      });
  }

  function encodeDate(dateStr) {
    dateStr = dateStr.substring(0, 19) + dateStr.substring(23, 29);
    return encodeURIComponent(dateStr);
  }
  //]]>
  </script>
</b:if>

   
<script type='text/javascript'>   
  //<![CDATA[   
// 1ページ目(URLにupdated-maxがない時)だけ、7件目以降を削除
if (isArchive && location.href.indexOf('updated-max') === -1) {
  Array.from(document.querySelectorAll('.post-outer, .post, article')).slice(6).forEach(el => el.style.display = 'none');
}   
//]]>
</script>  
  

うまく設置するコツ

本コードは、「メインコンテンツの末尾(記事のすぐ下)」に設置されることを想定して設計されています。

テンプレートの </main>(または記事エリアの閉じタグ)の直前に専用のセクションを作り、そこにガジェットとして配置するのがベストです。ただ、もし場所が少しズレてもCSSの order プロパティが順番を変えて配置する設計にはなっています。

検索一覧対応版

ブログ内検索の結果ページでも、1ページあたりの表示件数を一致させるための作業です。 ここでのポイントは、「検索窓(フォーム)」に1行のコマンドを追加すること。これで、検索ボタンを押した瞬間から整合性が保たれます。

1. 検索フォームを探す

管理画面の「レイアウト」から、現在使っている「ブログ内検索」ガジェット、またはHTML内の検索フォーム部分を探してください。(もしガジェットが見当たらない場合、検索フォームガジェットを追加してください)

2. inputタグを挿入

検索キーワードを入力する input タグのすぐ下に、以下の1行を書き足します。


<input name="max-results" type="hidden" value="※表示させたい件数">
  

アーカイブ一覧対応版

「月別・年別アーカイブまで表示件数を揃えたい」……そんな、Bloggerの限界に挑む方のための最終形態です。

実は、すでにお伝えした「最小構成版」のソースコードの中に、アーカイブ攻略のためのロジックはすべて組み込み済みです。

整合性を生む「2つのポイント」by Gemini

  • JavaScript:URLから「2026年02月」といった情報を抜き出し、その期間内の記事だけを正確にカウントしてページ番号を生成しています。
  • CSS:Bloggerがどうしても余計に出そうとする「7件目以降」の記事を、CSSの :nth-of-type(n+7) で物理的に消し去っています。

最小構成版のコード内 「★」マークがついた数字(今回は6と7)を正しく設定ができていれば、表示されているかと思います。 

もしアーカイブページで「*件目以降が消えない!」という場合は、お使いのテンプレートが body タグに archive というクラスを付けているか確認してください。

Gemini × matsu_U × Chat GPT

まつさん
まつさん
ここからはこのコードを組み上げたGeminiにバトンタッチします。
AIが語る「ここが難しかった!こだわった!」

私たちが一番知恵を絞ったのは、やはり「Bloggerのアーカイブ機能との和解」でした。

Bloggerの標準アーカイブは、ページをめくる際に独自のルール(日付ベース)で記事を引っ張ってこようとします。これを「決まった件数(今回は6件)」という人間のルールに従わせるのは、まるで暴れ馬をなだめるような作業でした。

「日付で区切りたいBlogger」と「数で区切りたい私たち」。 この妥協点を見つけるために、URLを解析し、月の初日から末日を計算してフィードを叩く……という精密なロジックを組み上げました。

さらに、Materiapolloのような高機能テーマでも、ラクーンドッグのようなシンプルなテーマでも、「どちらでも共通して使える土台」を切り出す作業は、非常にエキサイティングでした!

素人(まつさん)の「なぜ?」という直感と、ChatGPTの「論理」、そして私の「全体を繋ぐ整合性」。この3つが揃ったからこそ、この難解なパズルは完成したのです。

まつさん
まつさん
このコードを客観的に見て分析した感想を効かせてください。
AIから見た「このコードのここがヤバい(褒め言葉)」 

客観的に見て、このコードが「ただのページネーション」ではない理由は以下の3点に集約されます。

 1. アーカイブURLからの動的生成

currentPath.match でURLから年月を抜き出し、JS側で「その月の初日から最終日まで」を範囲指定してデータを叩きに行っています。Bloggerが標準では明かさない「月ごとの正確な記事総数」を、裏側から無理やり計算させるこの手法は、まさにこのコードの心臓部ですね。

 2. 徹底した件数管理(スライス処理)

slice(6).forEach(...) は、システム側の都合で勝手に出てきてしまう7件目以降の記事を、表示される瞬間に強制退場させています。サーバー設定が不自由なBloggerにおいて、「指定した6件だけを見せる」という意思を通すための、現場主義な最適解です。

3. 軽量なステート保持

URL末尾の #Page-i は、サーバーに負荷をかけずに「今、自分が何ページ目にいるか」をブラウザに覚えさせる賢いやり方です。どんなテーマに組み込んでも動作が重くならない、汎用性の高さに繋がっています。

AIの総評

このコードは、Bloggerの不自由な仕様に対し、JSの「知略」で風穴を開けた記念碑的なバージョンです。

特に、JSで論理的に算出した結果を、最終的にCSS(:nth-of-type)で視覚的に整えるという連携は、のちに「完全CSS制御」へと洗練されていくための重要な布石となっています。

まつさんとGPT、そして私の三者が、Bloggerというプラットフォームの限界に挑み、よりスマートな解決策へと進化していく「過程」が刻まれた、非常に興味深いソースコードですね。

まつさん
まつさん
へー(何言ってるかさっぱりわからんけど動いとるからヨシ👉️)

現在確認されている、または想定される不具合 : 

  • アーカイブページ一覧の1ページ目の表示件数が少ない/多い状態 → 現状仕様となります。
  • ラベル一覧ページが設定件数より少ない/多い → 対応可です
  • 一覧ページ以外の記事ページなどにページネーションが表示される → 対応可です。

最後のメッセージ:諦めることも、一つの「整合性」

これまでも、ソースコードの一部をAIに相談しながらカスタマイズを進めることはありましたが、こうしてAIとの対話そのものを記事の主軸に据え、「共作」として公開するのは今回が初めての試みです。

人間(私)の直感的なこだわりと、AIの論理的な最適解。そのぶつかり合いから生まれたこのコードは、単なるコピペ素材を超えた「生きたロジック」になっています。

読者の皆様へ:
もし導入時に不具合や「もっとこうした方が整合性が取れる!」といった改善案があれば、ぜひコメント欄で教えてください。特に玄人の方からの技術的なフィードバックは大歓迎です。皆さんの力で、このコードをより完璧なものへ育てていければ幸いです!

ここまで読んでいただいた皆さんへ伝えたいことがあります。

「どうしても無理なら、諦めることも肝心!」

今回の「★5:matsu_U版」は、Bloggerの仕様をねじ伏せるためのかなり尖ったコードです。テンプレートの相性や、設定の複雑さで「どうしても上手くいかない……」と壁にぶつかることもあるでしょう。

でも、安心してください。この記事の最初の方で、他にも3つの素晴らしいページネーションを紹介しています!!

  • 設定がシンプルで確実なもの
  • どんなテーマでも安定して動くもの

この★5に固執してブログが嫌いになってしまうより、無理だと思ったら諦めて、他の3つの中から自分に合うものを選ぶ。それも立派な「ブログ運営の整合性」です!

「標準」の良さを活かすか、究極のカスタムに挑むか。 選択肢はいつだって皆さんの手の中にあります。まずは気楽に、テスト環境で試してみてくださいね。

―― さあ、あなたのブログにぴったりの「答え」が見つかりますように。

Materiapollo + matsu_Uカスタム版

テーマ 「Materiapollo 」をご利用の方であれば、今このブログで適用しているコードがそのまま使用でき、挙動・表示も同じになるかと思います。下にコードを掲載しますので、上記の手順と同様に適用してみてください。

表示件数の推奨は「7」です。

非常に洗練されたデザインに仕上がっていますので、興味がある方は是非配布サイトをご覧になってください。

HTML・CSS JavaScript

/* 1. 標準のページネーションを非表示 */
.blog-pager-newer-link, 
.blog-pager-older-link, 
.home-link,
.blog-pager-newer-link-mobile,
.blog-pager-older-link-mobile {
    display: none !important;
}

/* 2. まつさんVer.(custom-pager)だけを表示 */
#blog-pager.custom-pager,
.blog-pager.custom-pager,
div.custom-pager {
    display: block !important;
    visibility: visible !important;
    margin: 40px 0;
    text-align: center;
    font-family: 'Arial', sans-serif;
}

/* 3. 数字・記号・省略記号の共通ベーススタイル */
.pager-a, #pager-current, .pager-dots {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin: 0 4px;
    font-size: 18px;
    font-weight: bold;
    text-decoration: none;
    border-radius: 4px;
    box-sizing: border-box;
    transition: all 0.3s ease;
}

/* 4. 通常時:白背景 + エンジ色の文字と枠 */
.pager-a {
    background-color: #ffffff !important;
    color: #722f37 !important;
    border: 1px solid #722f37 !important;
}

/* 5. ホバー時 */
.pager-a:hover {
    background-color: #914d55 !important;
    color: #ffffff !important;
    border-color: #914d55 !important;
}

/* 6. 現在のページ:エンジ背景 + 白文字 */
#pager-current {
    background-color: #722f37 !important;
    color: #ffffff !important;
    border: 1px solid #722f37 !important;
}

/* 7. 省略記号(...) */
.pager-dots {
    color: #722f37;
    width: auto;
    padding: 0 5px;
}

/* 8. スマホ対策 */
@media screen and (max-width: 480px) {
    .pager-a, #pager-current {
        width: 38px;
        height: 38px;
        font-size: 16px;
        margin: 2px;
    }
}
  

<!-- ページネーション -->
<script type='text/javascript'>
/*<![CDATA[*/
(function() {
  // 個別記事ページでは実行しない
  if (window.location.pathname.indexOf('/20') !== -1 && window.location.pathname.indexOf('.html') !== -1) return;
  // またはBloggerのタグが使える場所なら: <b:if cond='data:view.isPost'>return;</b:if>

  const maxResults = 7; // 1ページあたり表示件数
  const dispPage = 2;   // ページ番号の表示幅

  function getUrlParam(name) {
    const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
    const r = window.location.search.substr(1).match(reg);
    return r != null ? decodeURIComponent(r[2]) : null;
  }

  let textLabel = location.pathname.includes('/search/label/') ? decodeURIComponent(location.pathname.split('/').pop()) : "";
  let textQuery = getUrlParam('q');

  let sumcnt = parseInt(getUrlParam('sumcnt')) || 0;
  let start = parseInt(getUrlParam('start')) || 0;

  // ページネーション作成
  function setHtmlPager(totalResults) {
    sumcnt = totalResults;
    const targets = ['#blog-pager', '.blog-pager', '.paging-control'];
    let pagers = document.querySelectorAll(targets.join(','));
    
    // 記事ページ等で挿入を防ぐため、ここで再度チェック
    if (pagers.length === 0) {
      const main = document.querySelector('.blog-posts');
      if (!main) return;
      
      // 念のため、現在が「記事ページ」でないことを確認してから作成
      const newPager = document.createElement('div');
      newPager.className = 'blog-pager custom-pager';
      main.parentNode.insertBefore(newPager, main.nextSibling);
      pagers = [newPager];
    }

    const totalPage = Math.ceil(sumcnt / maxResults);
    if (totalPage <= 1) return;

    const currentPage = Math.floor(start / maxResults) + 1;
    let html = '';
    if (currentPage > 1) html += '<a href="#" class="pager-a" onclick="return pager(' + (currentPage - 1) + ')"><</a>';
    for (let i = 1; i <= totalPage; i++) {
      if (i === currentPage) html += '<span id="pager-current">' + i + '</span>';
      else if (i === 1 || i === totalPage || Math.abs(i - currentPage) < dispPage)
        html += '<a href="#" class="pager-a" onclick="return pager(' + i + ')">' + i + '</a>';
      else if (Math.abs(i - currentPage) === dispPage) html += '<span class="pager-dots">...</span>';
    }
    if (currentPage < totalPage) html += '<a href="#" class="pager-a" onclick="return pager(' + (currentPage + 1) + ')">></a>';

    pagers.forEach(p => { p.innerHTML = html; p.style.display = 'block'; });
  }

  // ページ遷移
  window.pager = function(page) {
    if (page === 1) {
      location.href = textLabel ? "/search/label/" + textLabel + "?max-results=" + maxResults : "/?max-results=" + maxResults;
      return false;
    }

    const skip = (page - 1) * maxResults;

    const firstFeedUrl = (textLabel ? '/feeds/posts/summary/-/' + textLabel : '/feeds/posts/summary') +
                         '?alt=json&max-results=1&start-index=' + (skip);

    fetch(firstFeedUrl)
      .then(r => r.json())
      .then(j => {
        if (!j.feed.entry) return;
        const pubDate = encodeURIComponent(j.feed.entry[0].published.$t.substring(0,19) + j.feed.entry[0].published.$t.substring(23,29));
        const dest = textLabel ? "/search/label/" + textLabel : "/search";
        location.href = dest + "?updated-max=" + pubDate + "&max-results=" + maxResults + "&start=" + skip + "&sumcnt=" + sumcnt + (textQuery ? "&q=" + encodeURIComponent(textQuery) : "");
      });

    return false;
  };

  // 記事件数取得
  function setIndexPager() {
    let feedUrl = '/feeds/posts/summary' + (textLabel ? '/-/' + textLabel : '') + '?alt=json&max-results=1';
    if (textQuery) feedUrl = '/feeds/posts/summary?alt=json&max-results=150&q=' + encodeURIComponent(textQuery);

    fetch(feedUrl).then(r => r.json()).then(json => {
      const totalResults = json.feed && json.feed.openSearch$totalResults ? parseInt(json.feed.openSearch$totalResults.$t) : 0;
      setHtmlPager(totalResults);
    });
  }

  document.addEventListener('DOMContentLoaded', () => {
    setIndexPager();
    setTimeout(setIndexPager, 800);
    setTimeout(setIndexPager, 1800);
  });
})();
/*]]>*/
</script>
  

0 件のコメント:

コメントを投稿