Bloggerの標準ナビゲーション(「前の投稿」「次の投稿」)に、物足りなさを感じていませんか?
ブログにおいて、ナビゲーションは読者を迷わせないための「地図」です。数字でページが並ぶ「ページネーション」を導入することは、単に便利になるだけでなく、実は大きなメリットがあります。
- 回遊率の向上: 「あと何ページあるか」が見えることで、読者が他の記事にも手を伸ばしやすくなります。
- デザインの自由度: カスタムページャーを採用することで、CSSを使ってブログの雰囲気に合わせた「自分だけのデザイン」に作り替えることが可能になります。
今回は、そんなBloggerカスタマイズの必須アイテムとも言えるページネーションについて、導入の仕組みが異なる「3つの王道パターン」と、表示件数の整合性にまでこだわった「究極の+1」を厳選して解説します。
あなたのブログをより使いやすく、より自分らしく仕上げるためのヒントになれば幸いです!
導入難易度 : ★★★☆☆
導入方法|HTML画面にペースト
特徴|高速 & 軽量
/ 無駄な処理が発生しない最適化設計 / ラベルページ 一覧 ◯ 対応 /
検索結果一覧ページ ◯ 対応 / アーカイブ一覧ページ × 非対応
導入難易度 : ★☆☆☆☆
導入方法|HTML/JavaScriptガジェットへペースト
特徴|コード準備と設置方法がシンプル
/ 3種類のデザイン / ラベルページ一覧 △ 一部テーマで非動作 /
検索結果一覧ページ △ 導入しただけでは非対応 / アーカイブ一覧ページ×
非対応
導入難易度 : ★★★★☆
導入方法|HTML編集画面でペースト
特徴|投稿フィードから総投稿数を取得し、ページリンクを計算
/ 必要なフィード範囲だけを検索し最適化 / ラベルページ一覧 △ /
検索結果一覧ページ △ / アーカイブ一覧ページ × 非対応
※
あらかじめ検索結果・ラベルページの表示件数を合わせる作業が必要
matsu_Uカスタム版
カスタマイズの経緯
本カスタム版は、Blogger界隈で多くの知見を公開されている IB-Noteさんのコード をベースに、私のブログ環境に合わせて微調整を加えたものです。
私自身はWeb制作の実務経験はありませんが、ChatGPTやGeminiとの対話を通じて、ロジックを一つずつ紐解きながら形にしていきました。
AIの助けを借りて、素人なりに「納得できる整合性」を少しずつ積み上げた結果です。素晴らしい先人たちのコードと並んで、「自分に合ったページネーション選び」のひとつの選択肢にあがれば嬉しいです。
どうにかできないかと試行錯誤した「こだわり」
このカスタム版を作る上で、私が一番に考えたのは、「ブログ全体の徹底的な整合性」です。 Bloggerを使っていると、ページによって表示件数が微妙にズレたりすることがありますが、その現象を「どうにか解決できないかな……」と模索したのが始まりでした。
- 表示件数をどこまでも揃える:トップページ、ラベル一覧、検索結果のすべてで、設定した件数がピタリと維持されるよう調整を重ねました。
- アーカイブ一覧への挑戦:月別・年別などの「アーカイブ一覧」でも表示件数を揃えたかったのですが、自分なりに探した限りでは同様の実装例が見当たらなかったため、AIと一緒に「どうにかできないか」と知恵を絞ってロジックを組み込みました。
- 素人目線の「なぜ?」を大切に:Web制作の実務経験がないからこそ感じた「なぜここがズレるのか?」という素朴な疑問をAI(ChatGPT/Gemini)にぶつけ、納得いくまで調整を繰り返しました。
専門的な知識がなくても、AIという相棒と向き合うことで、自分なりに満足のいく形を完成させることができました。「どこをめくっても、常に決まった件数が並ぶ」――そんな当たり前の心地よさを、ぜひ試してみてください。
導入難易度 : ★★★★★
導入方法|HTML画面でセクションを作成後、コードをペーストして設置
特徴|AIと素人がIB-Noteさんのコードをベースに改変 / ラベル一覧ページ ◯ 対応 / 検索結果一覧ページ ◯ オプションで対応 / アーカイブ一覧ページ △ 一部対応
導入する前に必ずテーマのバックアップを取ってください!!
お兄さんとの約束ダゾ!!
テスト・検証環境について
本コードは、以下の環境で動作確認を行っていますが、それぞれ「どこまで手を加えたか」に違いがあります。
-
ラクーンドッグ|たぬき男の話
今回紹介している最小構成のコードで動作するはずです。 -
Materiapollo|Cottpic
当ブログ(https://blogger.matsusanjpn.com)で実装運用中。
※こちらは多機能なため、テーマ独自の挙動と競合しないよう、紹介しているコードにさらに追加の調整コードを追加した状態での動作を確認しております。
シンプルな環境と、カスタム環境の両方で「整合性」が取れることを確認済みでではあります。
ソースコードと導入方法
本カスタム版は、ご利用の環境や「どこまで整合性を求めるか」に合わせて、以下の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パターンすべてで必要なコードです)
<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^='Blog'] {
order: 1 !important;
}
/* ページャー(HTML2)を2番目に(下) */
#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 = "<data:blog.homepageUrl.canonical/>";
const label = "<b:eval expr='data:blog.searchLabel'/>";
const query = "<b:eval expr='data:blog.searchQuery'/>";
const selector = '.pagination';
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にバトンタッチします。
私たちが一番知恵を絞ったのは、やはり「Bloggerのアーカイブ機能との和解」でした。
Bloggerの標準アーカイブは、ページをめくる際に独自のルール(日付ベース)で記事を引っ張ってこようとします。これを「決まった件数(今回は6件)」という人間のルールに従わせるのは、まるで暴れ馬をなだめるような作業でした。
「日付で区切りたいBlogger」と「数で区切りたい私たち」。 この妥協点を見つけるために、URLを解析し、月の初日から末日を計算してフィードを叩く……という精密なロジックを組み上げました。
さらに、Materiapolloのような高機能テーマでも、ラクーンドッグのようなシンプルなテーマでも、「どちらでも共通して使える土台」を切り出す作業は、非常にエキサイティングでした!
素人(まつさん)の「なぜ?」という直感と、ChatGPTの「論理」、そして私の「全体を繋ぐ整合性」。この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」です。
非常に洗練されたデザインに仕上がっていますので、興味がある方は是非配布サイトをご覧になってください。
/* 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>

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