【第1回】Bloggerにページナビゲーションをつけるよ!
ページナビゲーションは、上図のように記事の一覧など1ページに収まらない場合に、複数のページに分けて表示する機能です。
残念ながらBloggerには、このページナビゲーションの機能がありません。前後の記事へのナビゲーションの機能はあるのですが、ページをナビゲーションする機能はないのですよありません。
ということで、今回はこのページナビゲーションの機能をJavaScriptで作成してみたいと思います。
まずは、ページナビゲーションを実現するために、必要な情報と実装方法をまとめていきます。
「解説はいらないからソースコードはよ!」という方は、ソースコードまで飛んでください。
ページナビゲーションを実現するために必要な情報
ページナビゲーションを実現するにあたり、まずは必要な情報を洗い出してみます。ざっと挙げてみると以下のような情報が必要ですね。
- 記事の総数
- ページの総数を算出するために必要になります。
- 1ページに表示する記事数
- これは自分で決める数字です。Bloggerの投稿ページのデフォルトは7ですので、この前後の数字が良いでしょう。
- ページの総数
- ページの総数は「記事の総数 ÷1ページに表示する記事数」で求めます。
- 各ページのURL
- ページ番号のリンクにつけるURLです。
- 現在のページ番号
- 現在表示しているページ番号です。必須ではありませんが、現在表示しているページ番号だけ色を変えたり、「前のページへ」「次のページへ」のような機能をつけたい場合に必要になります。
- 今回は、現在表示しているページ番号にはリンクをつけないようにします。
記事の総数
ページの総数を算出するために必要になります。すべての記事の情報が必要になるので、「投稿」ガジェットのデータタグなどからの算出も厳しいです。
そこで、JavaScriptでフィード情報をリクエストして取得します。
フィードはRSSなどで使用される情報で、HTMLリクエストで記事の情報を取得できる仕組みを使用します。
これを使用すると、記事の情報をAtomやJsonといった形式で取得ができます。取得にはJavaScriptのfetch()を使ってます。
// フィードURLを作成する。
var feedUrl = blogUrl + 'feeds/posts/summary';
feedUrl += '?orderby=published';
feedUrl += '&max-results=';
feedUrl += maxResults;
feedUrl += '&alt=json';
// フィードを取得する。
fetch(feedUrl)
.then((response) => {
console.log(feedUrl);
if(response.ok) {
return response.json();
} else {
throw new Error();
}
})
.then(function(json) {
console.log(json);
// ページ総数を算出する。
var totalResults = parseInt(json.feed.openSearch$totalResults.$t, 10);
1ページに表示する記事数
これは自分で決める数字です。今回はソースコードにべた書きにしてます。
var maxResults = 7;
ページの総数
ページ番号を表示するには、ページの総数が必要となります。ページの総数は「がないと、何ページ分のリンクを作ったらよいかわからないためです。
ページの総数は「ページの総数 = 記事の総数 ÷1ページに表示する記事数」で求めることができます。
var totalPage = parseInt(totalResults / maxResults );
if ((totalResults % maxResults ) > 0)
{
totalPage += 1;
}
表示する記事が1ページに表示する記事数に満たない場合でも、少なくとも1ページは表示するため、2~5行目の処理があります。
各ページのURL
ページナビゲーションを実現するためのキモになります。
各ページへリンクをはって、クリックしたらそのページを表示させる必要があります。
例えば、1ページに7記事表示させるのであれば、「1ページ」をクリックしたら1記事目~7記事目を、「2ページ」をクリックしたら、8記事目~14記事目を表示させます。
これを実現させるために、Blogger検索のURLの仕組みを使います。
URLの仕組み
このブログにある「検索」で「blogger」を検索してみてください。すると「blogger」に関連する記事が一覧で表示されると思います。
その時のURLを見てみてください。下のようになっていると思います。
```
https://bgt-48.blogspot.jp/search?q=blogger
ブログのドメインの後が`search?q=blogger`となっています。「検索」はキーワード検索なので、`?q=キーワード`の形になっています。このようにURLにパラメータを与えると特定の投稿のみに絞り込むことができます。
「1記事目~7上記はキーワードによる記事の絞り込みですが、投稿日時や取得する記事数による絞り込みも可能です。この投稿日時と記事数の絞り込みによって「1記事目~7記事目だけ取得する」「8記事目~14記事目だけ取得する」ということが可能になります。
### URLのパラメータ
具体的には、以下のパラメータを使いって1ページに表示する記事に絞り込みます。
複数のパラメータを指定する場合は、以下のように`&`でつなげて指定します。
- orderby
- `published`を指定します。これによって投稿日順に表示されます。
- updated-max
- 表示する投稿の一番新しい日付をしていします。日付のフォーマットは、RFC3339の形式(例:'2005-08-09T10:57:00-08+09:00')です。これを指定すると、この日付までの投稿が表示されます。
- 日付は、フィード情報から取得します。
- max-result
- 検索結果の最大数を指定します。1記事目~7記事目であれば最大7記事取得したいので7になります。
ここで重要
ちょっとわかりづらいですね。例をあげましょう。
以下のような10個の記事があったとします。これを1ページに5記事ずつ表示したいとします。
No | 投稿日時
-------- | -----
1 | 2019-01-01 12:00:00
2 | 2019-01-11 12:00:00
3 | 2019-01-21 12:00:00
4 | 2019-01-31 12:00:00
5 | 2019-02-01 12:00:00
6 | 2019-02-11 12:00:00
7 | 2019-02-21 12:00:00
8 | 2019-03-01 12:00:00
9 | 2019-03-11 12:00:00
10 | 2019-03-21 12:00:00
1ページに5記事ずつ表示したいわけですので、1ページ目に表示する記事はNo1~5、2ページ目に表示する記事はNo6~10です。
No.1~5で一番新しい記事の投稿日時は「2019-01-01 12:00:00」ですので、この投稿日時までの5記事分を表示させればよいですね。
なので1ページ目のURLは以下のようになります。
同様に2ページ目のURLは以下のようになります。
ここで問題なのが`update-max`パラメータになります。この`update-max`パラメータに、そのページに表示する投稿の一番新しい日付を指定してあげる必要があります。ますが、実際にどうやって求めればよいのでしょうか?
これについては、次の章で説明します。
### 各ページ内で最新投稿の日付の取得方法
ページ内の最新の投稿の日付はフィード情報から取得します。
フィード情報を1ページに表示する件数分だけ取得し、そのフィード情報の中から最新の投稿の日付を取得します。
これを、ページ数分だけ繰り返すことで、各ページ内で最新の投稿の日付を取得することができます。
## ソースコード
おまたせしました。ソースコードです。
上の解説をみながら、ソースを読んでみてください。
```html
<!-- pager -->
<b:if cond='data:blog.pageType == "index"'>
<div class='blogger-pager'></div>
<script type='text/javascript'>var blogUrl = "<data:blog.homepageUrl/>"</script>
<script type='text/javascript'>var label = "<data:blog.searchLabel/>"</script>
<script type='text/javascript'>
//<![CDATA[
var selector = '.blogger-pager';
var maxResults = 7;
insertPagerLink(selector, maxResults);
//
// 指定したセレクタにページナビゲーションを挿入する
//
function insertPagerLink(selector, maxResults)
{
// フィードURLを作成する。
var feedUrl = blogUrl;
feedUrl += 'feeds/posts/summary';
feedUrl += '?orderby=published';
feedUrl += '&max-results=';
feedUrl += maxResults;
feedUrl += '&alt=json';
// フィードを取得する。
fetch(feedUrl)
.then((response) => {
console.log(feedUrl);
if(response.ok) {
return response.json();
} else {
throw new Error();
}
})
.then(function(json) {
console.log(json);
// URLからカレントのページ番号を算出する。
var currentUrl = location.href;
var currentPage = 1;
if (currentUrl.indexOf("#Page-") > -1)
{
var no = currentUrl.substring(currentUrl.indexOf("#Page-")+6);
currentPage = parseInt(no);
}
console.log('currentPage=' + currentPage);
// ページ総数を算出する。
var totalResults = parseInt(json.feed.openSearch$totalResults.$t, 10);
var startIndex = parseInt(json.feed.openSearch$startIndex.$t, 10);
var itemsPerPage = parseInt(json.feed.openSearch$itemsPerPage.$t, 10);
var totalPage = parseInt(totalResults / maxResults );
if ((totalResults % maxResults ) > 0)
{
totalPage += 1;
}
console.log('totalPage=' + totalPage);
// ページナビゲーションを作成する。
var pager = '';
for (i = 1; i <= totalPage; ++i)
{
if (i == currentPage) {
pager += '<span class="current-page">' + i + '</span>';
}
else {
pager += '<a id="Page-' + i + '"' + ' href=""><span class="page">' + i + '</span></a>';
}
}
// HTMLへ書き出す。
var pagerElem = document.querySelector(selector);
if (null != pagerElem)
{
pagerElem.innerHTML = pager;
}
// ページナビゲーションにURLを設定する。
for (i = 1; i <= totalPage; ++i)
{
if (i == currentPage) continue;
setTargetPageUrl(selector, blogUrl, label, i, maxResults);
}
})
.catch((error) => {
console.log(error)
});
}
//
// 指定したページのURLを設定する
//
function setTargetPageUrl(selector, blogUrl, label, targetPage, maxResults)
{
var href = blogUrl;
// 指定したページのインデックスを算出する。
// インデックスは1開始なので、0以下の場合は1とする。
var startIndex = (targetPage - 1) * maxResults;
if (startIndex <= 0) startIndex = 1;
// フィードURLを作成する。
var feedUrl = blogUrl;
feedUrl += 'feeds/posts/summary';
if (label != '')
{
// カテゴリページの場合はlabelをつける
feedUrl += '/-/' + label;
}
feedUrl += '?orderby=published';
feedUrl += '&start-index=';
feedUrl += startIndex;
feedUrl += '&max-results=';
feedUrl += maxResults;
feedUrl += '&alt=json';
// フィードを取得する。
fetch(feedUrl)
.then((response) => {
console.log(feedUrl);
if(response.ok) {
return response.json();
} else {
throw new Error();
}
})
.then(function(json) {
console.log(json);
// 一番新しい記事の日付を取得する。
var date = encodeDate(json.feed.entry[0].published.$t);
// ページURLを作成する。
href += 'search';
if (label != '')
{
// カテゴリページの場合はlabelをつける
href += '/label/' + label;
}
href += '?orderby=published';
href += '&max-results=';
href += maxResults;
href += '&updated-max=' + date;
href += '#Page-' + targetPage;
// hrefにURLを設定する。
var liSelector = selector + ' #Page-' + targetPage;
var liElem = document.querySelector(liSelector);
if (null != liElem)
{
liElem.href = href;
}
})
.catch((error) => {
console.log(error)
});
}
//
// 日付をRFC3339タイムスタンプ形式で取得する。
//
function encodeDate(dateStr)
{
dateStr = dateStr.substring(0, 19) + dateStr.substring(23, 29);
return encodeURIComponent(dateStr);
}
//]]>
</script>
</b:if>
参考サイト
フィードの取得する際のクエリと取得できるデータのフォーマットについて、Googleのヘルプに詳細があります。こちらも参考にしてください。
Google Data APIs #DocumentFormat
最後まで読んでいただき、ありがとうございます。
また読んでくださいませ。
そんじゃーね。