2018年11月 1日

Bloggerのpagination

Bloggerをいじっている。
デフォルトのものや、Vaster2も含めてPaginationが無いということで、いろいろと記事があったので、それを真似してみた。
また、ラベルのページだとうまくいかないので、HTMLを治すべしというコメントも見つけた。

試行錯誤でやっているので、間違えているかもしれないが、今わかっている範囲でメモ。

なお、一般公開していないと/feeds/posts/summary へのアクセスができないため、paginationが表示されない。

テンプレートの<b:skin> ~ <b:/skin>の中に入れる。(カスタマイズの上級者向けの中のCSSの追加から追加するとエラーになる)

       <Group description="ページナビ" selector="#blog-pager">
          <Variable name="pager.color" description="文字の色" type="color" default="#666666" value="#666666"/>
          <Variable name="pager.background" description="背景の色" type="color" default="#ffffff" value="#ffffff"/>
          <Variable name="pager.border" description="周囲の線" type="color" default="#dedede" value="#dedede"/>
          <Variable name="pager.color.hover" description="文字の色(カーソル時)" type="color" default="#ffffff" value="#ffffff"/>
          <Variable name="pager.background.hover" description="背景の色(カーソル時)" type="color" default="#008ec2" value="#008ec2"/>
        </Group>

カスタマイズの上級者向けの中のCSSの追加から追加してもよいし、テンプレートの<b:skin> ~ <b:/skin>の中に入れてもよい。

        #blog-pager {overflow: visible}

        .showpageOf {display:none}

        span.showpagePoint {
          display: inline-block;
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
          padding: .6em .8em;
          margin: 0 .2em;
          border: 1px solid $(pager.border); /* ページナビの現在ページの枠線の色 */
          border-radius: 0.25em;
          background-color: $(pager.background);  /* ページナビの色を変えるにはここを変える */
          border-color: $(pager.border);      /* ページナビの色を変えるにはここを変える */
          color: $(pager.color); /* ページナビの現在ページの文字の色 */
          pointer-events: none;
        }

        span.showpageNum, span.showpage {
          margin: 0 .2em;
        }

        .showpageNum a, .showpage a, div.blog-pager a.blog-pager-newer-link, div.blog-pager a.blog-pager-older-link, div.blog-pager a.home-link {
          display: inline-block;
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
          padding: .6em .8em;
          border: 1px solid $(pager.border); /* ページナビの現在ページ以外の枠線の色 */
          border-radius: 0.25em;
          color: $(pager.color); /* ページナビの現在ページ以外の文字の色 */
          text-decoration: none;
        }

        #blog-pager a:hover{
          text-decoration:none;
          color:$(pager.color.hover);
          background:$(pager.background.hover);
        }

レイアウトのページでHTML/Javascriptで入れる。もしくは、テンプレートの</body>の前に入れる。
https://medium.com/mr-blogger-tricks/how-to-add-next-previous-numbered-pagination-in-blogger-7f37a453fe79 にあったものをベースに変更を加えた

<b:if cond='data:blog.pageType != &quot;item&quot;'>
<b:if cond='data:blog.pageType != &quot;static_page&quot;'> 

<div id="blog-pager-script-load-point"></div>
<script type='text/javascript'>
  /*<![CDATA[*/
  var post_per_page = 5; // 1画面に表示する投稿の数
  var button_per_page = 2; // 表示するページ数(各ページへのボタンの数)
  var upPageWord = '« Previous Page';
  var downPageWord = 'Next Page »';
  var root_dir = "/"; 
  /*]]>*/
</script>
<script type='text/javascript'>
  /*<![CDATA[*/
  var page_num_tmp;
  var pagetype;
  var current_page;
  var labelname;
  pageblogger();

  function looppage(amount_of_posts) {
    // 引数は投稿の総件数
    var html = '';

    // 現在のページの左側に何ページ分のボタンを表示するか
    num_left_pages = parseInt(button_per_page / 2);
    if (num_left_pages == button_per_page - num_left_pages) {
      button_per_page = num_left_pages * 2 + 1
    }

    // 一番左端のボタンに表示するページ番号を決定する
    most_left_page = current_page - num_left_pages;
    if (most_left_page < 1) most_left_page = 1;

    // 全投稿数に対して何ページ必要かを決定する
    num_required_pages = parseInt(amount_of_posts / post_per_page) + 1;
    if (num_required_pages - 1 == amount_of_posts / post_per_page) num_required_pages = num_required_pages - 1;

    // 右端のページ番号を決定
    most_right_pagenum = most_left_page + button_per_page - 1;
    if (most_right_pagenum > num_required_pages) most_right_pagenum = num_required_pages;

    // HTMLの作成
    html += "<span class='showpageOf'>Page " + current_page + ' of ' + num_required_pages + "</span>";

    // 1ページ前のページ番号
    var prev_page = parseInt(current_page) - 1;

    if (current_page > 1) {
      if (current_page == 2) {
        if (pagetype == "page") {
          html += '<span class="showpage"><a href="' + root_dir + '">' + upPageWord + '</a></span>'
        } else {
          html += '<span class="showpageNum"><a href="/search/label/' + labelname + '?&max-results=' + post_per_page + '">' + upPageWord + '</a></span>'
        }
      } else {
        if (pagetype == "page") {
          html += '<span class="showpageNum"><a href="#" onclick="redirectpage(' + prev_page + ');return false">' + upPageWord + '</a></span>'
        } else {
          html += '<span class="showpageNum"><a href="#" onclick="redirectlabel(' + prev_page + ');return false">' + upPageWord + '</a></span>'
        }
      }
    }
    if (most_left_page > 1) {
      if (pagetype == "page") {
        html += '<span class="showpageNum"><a href="' + root_dir + '">1</a></span>'
      } else {
        html += '<span class="showpageNum"><a href="/search/label/' + labelname + '?&max-results=' + post_per_page + '">1</a></span>'
      }
    }
    if (most_left_page > 2) {
      html += ' ... '
    }
    for (var jj = most_left_page; jj <= most_right_pagenum; jj++) {
      if (current_page == jj) {
        html += '<span class="showpagePoint">' + jj + '</span>'
      } else if (jj == 1) {
        if (pagetype == "page") {
          html += '<span class="showpageNum"><a href="' + root_dir + '">1</a></span>'
        } else {
          html += '<span class="showpageNum"><a href="/search/label/' + labelname + '?&max-results=' + post_per_page + '">1</a></span>'
        }
      } else {
        if (pagetype == "page") {
          html += '<span class="showpageNum"><a href="#" onclick="redirectpage(' + jj + ');return false">' + jj + '</a></span>'
        } else {
          html += '<span class="showpageNum"><a href="#" onclick="redirectlabel(' + jj + ');return false">' + jj + '</a></span>'
        }
      }
    }
    if (most_right_pagenum < num_required_pages - 1) {
      html += '...'
    }
    if (most_right_pagenum < num_required_pages) {
      if (pagetype == "page") {
        html += '<span class="showpageNum"><a href="#" onclick="redirectpage(' + num_required_pages + ');return false">' + num_required_pages + '</a></span>'
      } else {
        html += '<span class="showpageNum"><a href="#" onclick="redirectlabel(' + num_required_pages + ');return false">' + num_required_pages + '</a></span>'
      }
    }

    // 1ページ後のページ番号
    var next_page = parseInt(current_page) + 1;

    if (current_page < num_required_pages) {
      if (pagetype == "page") {
        html += '<span class="showpageNum"><a href="#" onclick="redirectpage(' + next_page + ');return false">' + downPageWord + '</a></span>'
      } else {
        html += '<span class="showpageNum"><a href="#" onclick="redirectlabel(' + next_page + ');return false">' + downPageWord + '</a></span>'
      }
    }

    // pagerを見つけ出して、そこにHTMLを書き出す
    var pageArea = document.getElementsByName("pageArea");
    var blogPager = document.getElementById("blog-pager");
    for (var p = 0; p < pageArea.length; p++) {
      pageArea[p].innerHTML = html
    }
    if (pageArea && pageArea.length > 0) {
      html = ''
    }
    if (blogPager) {
      blogPager.innerHTML = html
    }
  }
  //  function looppage(amount_of_posts) ここまで

  function counttotaldata(root) {
    // 引数の中には、投稿のサマリがJSON形式で入っている。
    var feed = root.feed;
    // 引数で与えられたJSONデータの中から、(root).feed.openSearch$totalRults.$tの
    // データ(投稿の総数)を読み出し、10進数で整数に変換
    var totaldata = parseInt(feed.openSearch$totalResults.$t, 10);
    // 投稿の総数を引数にしてlooppage関数(このjavascriptの中で定義している)を呼び出す。
    looppage(totaldata)
  }

  function pageblogger() {
    var thisUrlHost = location.host;
    var thisUrlPath = location.pathname;
    // URLのパラメータの最初の?は不要
    var thisUrlParam = location.search.substring(1);
    var thisUrlParams = [];
    thisUrlParam.split('&').forEach(function(value){
      thisUrlParams[value.split('=')[0]] = value.split('=')[1];
    });
    var thisUrlHash = location.hash;

    if (thisUrlPath.indexOf("/search/label/") != -1) {
      // ラベルのページの場合
      // /serch/label/以降のラベル名を取得
      labelname = thisUrlPath.substring(thisUrlPath.indexOf("/search/label/") + 14)
    }
    if ( thisUrlPath.substring(thisUrlPath.length - 5) !== ".html") {
      //URL Pathが.htmlがで終端していない場合
      if (!("q" in thisUrlParams) ) { 
        //URLのパラメータに"q"(ブログ内検索の検索文字列)がない場合
        if (thisUrlPath.indexOf("/search/label/") == -1) {
          //ラベルのページではない場合

          if (thisUrlPath.indexOf("/search") == -1) {
          //検索ページだけど、q(=検索文字列)がついていない場合
            if (!("max-results" in thisUrlParams)) {
              // max-resultsがついていないと、デフォルトの閾値の25件(だと思う)で表示され、
              // 1ページ目に投稿がたくさん表示されるので、閾値を指定してリロードする。
              location.href = "//" + thisUrlHost + thisUrlPath + "?max-results=" + post_per_page  
                + (location.search.length > 0 ? "&" + location.search.substring(1) : "");
            }
          }

          pagetype = "page"
          if (thisUrlHash.indexOf("#PageNo=") != -1) {
            //#PageNo=がある場合、そこからURLの最後までを取り出す
            current_page = parseInt( thisUrlHash.substring(thisUrlHash.indexOf("#PageNo=") + 8, thisUrlHash.length) , 10);
            if (isNaN(current_page)) current_page = 1;
          } else {
            //#PageNo=がない場合、1を指定
            current_page = 1
          }
          // root_dirはURLの直下(="/")
          // 投稿のサマリをJSON形式で取り出すAPIを呼び出すように、ScriptタグをHTMLに書き出す
          // 取り出したJSONデータは、counttotaldata関数の引数となるように指定しており、
          // 下記が呼び出された際に、counttotaldata関数(このjavascriptの中で定義している)が呼び出される。
          //document.write("<script src=\"" + root_dir + "feeds/posts/summary?max-results=1&alt=json-in-script&callback=counttotaldata\"><\/script>")
          var call_post_summary = document.createElement("script");
          call_post_summary.async = true;
          call_post_summary.src = root_dir + "feeds/posts/summary?max-results=1&alt=json-in-script&callback=counttotaldata";
          document.getElementById("blog-pager-script-load-point").appendChild(call_post_summary);
        } else {
          //ラベルのページの場合
          pagetype = "label";
          if (thisUrlParams["max-results"]) {
            // max-resultsが指定されている場合
            post_per_page = thisUrlParams["max-results"];
          } else {
            // max-resultsがついていないと、デフォルトの閾値の25件(だと思う)で表示され、
            // 1ページ目に投稿がたくさん表示される。、2ページ以降はpost_per_pageで指定した値になるので、
            // ダブリが出てしまう。
            location.href = "//" + thisUrlHost + thisUrlPath + "?max-results=" + post_per_page  
              + (location.search.length > 0 ? "&" + location.search.substring(1) : "");
          }
          if (thisUrlHash.indexOf("#PageNo=") != -1) {
            //#PageNo=がある場合、そこからURLの最後までを取り出す
            current_page = parseInt( thisUrlHash.substring(thisUrlHash.indexOf("#PageNo=") + 8, thisUrlHash.length) , 10);
            if (isNaN(current_page)) current_page = 1;
          } else {
            //#PageNo=がない場合、1を指定
            current_page = 1
          }
          // ラベルのサマリをJSON形式で取り出すAPIを呼び出すように、ScriptタグをHTMLに書き出す
          // 取り出したJSONデータは、counttotaldata関数の引数となるように指定しており、
          // 下記が呼び出された際に、counttotaldata関数(このjavascriptの中で定義している)が呼び出される。
          //document.write('<script src="' + root_dir + 'feeds/posts/summary/-/' + labelname + '?alt=json-in-script&callback=counttotaldata&max-results=1" ><\/script>')
          var call_post_summary = document.createElement("script");
          call_post_summary.async = true;
          call_post_summary.src = root_dir + 'feeds/posts/summary/-/' + labelname + '?alt=json-in-script&callback=counttotaldata&max-results=1';
          document.getElementById("blog-pager-script-load-point").appendChild(call_post_summary);
        }
      } else {
        //URLのパラメータに"q"(ブログ内検索の検索文字列)がある場合
        pagetype = "search";
        var search_first_item;
        var search_by_date;
        var search_redirect_required = false;

        if (thisUrlParams["max-results"]) {
          // max-resultsが指定されている場合
          post_per_page = thisUrlParams["max-results"];
        } else {
          search_redirect_required = true;
        }
        if (thisUrlParams["start"]) {
          // startは検索結果に表示されている一番上の結果が、何番目の結果かを示す。カウントは0からスタートする(配列のインデックス番号と同じ)。
          search_first_item = thisUrlParams["start"];
        } else {
          search_redirect_required = true;
          search_first_item = 0;
        }
        if (thisUrlParams["by-date"]) {
          // by-dateがtrueの場合は日付順、falseの場合は関連順に結果を表示
          search_by_date = thisUrlParams["by-date"];
        } else {
          search_redirect_required = true;
          search_by_date = 0;
        }
        if ( search_redirect_required ) {
          // 必要なパラメータが付いていない場合は付けさせるために再読込させる
          // (特にmax-resultsがついていないと、デフォルトの閾値の25件(だと思う)で表示され、
          //  1ページ目に投稿がたくさん表示される。、2ページ以降はpost_per_pageで指定した値になるので、
          //  ダブリが出てしまう。)
          location.href = "//" + thisUrlHost + thisUrlPath 
            + "?q=" + thisUrlParams["q"] 
            + "&max-results=" + post_per_page 
            + "&start=" + search_first_item 
            + "&by-date=" + search_by_date;
        }
      }
    }
  }

  function redirectpage(page_num) {
    //  function looppage()関数で生成されるHTMLから呼び出される
    jsonstart = (page_num - 1) * post_per_page;
    page_num_tmp = page_num;
    var nBody = document.getElementsByTagName('head')[0];
    var newInclude = document.createElement('script');
    newInclude.type = 'text/javascript';
    // 投稿のサマリをJSON形式で取り出すAPIを呼び出すように、ScriptタグをHTMLに書き出す
    // 取り出したJSONデータは、finddatapost関数の引数となるように指定しており、
    // 下記が呼び出された際に、finddatapost関数(このjavascriptの中で定義している)が呼び出される。
    newInclude.setAttribute("src", root_dir + "feeds/posts/summary?start-index=" + jsonstart + "&max-results=1&alt=json-in-script&callback=finddatepost");
    nBody.appendChild(newInclude)
  }

  function redirectlabel(page_num) {
    //  function looppage()関数で生成されるHTMLから呼び出される
    jsonstart = (page_num - 1) * post_per_page;
    page_num_tmp = page_num;
    var nBody = document.getElementsByTagName('head')[0];
    var newInclude = document.createElement('script');
    newInclude.type = 'text/javascript';
    // ラベルのサマリをJSON形式で取り出すAPIを呼び出すように、ScriptタグをHTMLに書き出す
    // 取り出したJSONデータは、finddatapost関数の引数となるように指定しており、
    // 下記が呼び出された際に、finddatapost関数(このjavascriptの中で定義している)が呼び出される。
    newInclude.setAttribute("src", root_dir + "feeds/posts/summary/-/" + labelname + "?start-index=" + jsonstart + "&max-results=1&alt=json-in-script&callback=finddatepost");
    nBody.appendChild(newInclude)
  }

  function finddatepost(root) {
    post = root.feed.entry[0];
    var timestamp1 = post.published.$t.substring(0, 19) + post.published.$t.substring(23, 29);
    var timestamp = encodeURIComponent(timestamp1);
    var address = ""
    if (pagetype == "page") {
      var address = "/search?updated-max=" + timestamp + "&max-results=" + post_per_page + "#PageNo=" + page_num_tmp
    } else {
      var address  = "/search/label/" + labelname + "?updated-max=" + timestamp + "&max-results=" + post_per_page + "#PageNo=" + page_num_tmp
    }
    location.href = address 
  }
  /*]]>*/
</script>

</b:if>
</b:if>


ラベルページでは、テンプレートに以下のような手を加えるように書かれていることが多いが、上記では、多分やらなくても大丈夫。
テンプレートを検索して
<a expr:href='data:label.url'><data:label.name/></a>
という感じの行を探す
「expr:href='data:label.url'」を探す

これを
<a expr:href='data:label.url + "?max-results=5"'><data:label.name/></a> という感じに置き換える
「 + "?max-results=5"」を付け足すのがポイント

コメントする