Bloggerの関連記事表示を強化する

この記事は、既に最新ではありません。最新の関連記事は、「Blogger用の関連記事表示機能:FeedRelatedPosts.js」を使用しています。


Bloggerに関連記事を表示する」の続きです。

「A Simple Related Posts Widget For Blogger」の問題点

  • 関連度が低い
    • 完全なランダムの連続から偏ったランダムに修正する
      • 複数のラベルで重複している記事の表示確率を上げる
        • 重複記事は関連度が高いものと仮定する
      • コメントの記載のある記事の表示確率を上げる
        • コメントのある記事は、人気があるものと仮定する
    • 関連度が良くなったわけではない (´・ω・`)
  • 同期読込みのため、ページの読込みをブロックする
    • 非同期(async)で読込むように修正する
    • 非同期の読込みで、最後のRSSフィードを読込んだ段階で関連記事を出力する
    • 表示速度向上
  • RSSフィードの記事内容をすべて読込むため、通信量が増える
    • 概要のみ読込むように修正する
      • /feeds/posts/default//feeds/posts/summary/
    • 最新の10件のみ読み込む仕様はそのままとする
  • 関連記事が1つだとヘッダ部分が表示されない
    • 関連記事が1つでもヘッダ部分を表示するように修正する

補足

A Simple Related Posts Widget For Bloggerでは、画像を表示できませんでした。ですが、Customizable Related Posts Widget for Bloggerであれば、画像を表示できるようです。

筆者は、画像表示の必要がなかったため、下記のコードでは非対応です。ですが、RSSフィード上に画像のurlが出力されていることまでは確認しました。画像urlの収集箇所はコメントアウトしてあるため、好みに合わせて修正ください。

修正結果

修正と言いながら、概念は同じでも全取っ替えです。

結果

サンプル
※FontAwesome部分のみ下記コードと異なります。

style

#related-posts {
  margin: 0.5em 0;
  border: 1px solid #aaa;
  display: none;
}
#related-posts h4 {
  font-size: 1.25em;
  font-weight: normal;
  color: #fff;
  background-color: #888;
  margin: 0;
  border: none;
}
#related-posts ul {
  padding: 0;
  list-style-type: none;
  background: #f9f9f9;
  margin: 0;
}
#related-posts li {
  padding: 0;
}
#related-posts li a {
  display: block;
  text-decoration: none;
  padding: 3px 1em 3px 2.5em;
  border-bottom: 1px solid #ddd;
}
#related-posts li a:hover {
  background: #eee;
}
#related-posts li:last-child a{
  border-bottom: none;
}

※BloggerテンプレートなどのCSSに依存します。配置環境毎に修正が必要です。

js

(function(root, factory) {
  if (!root.RelatedPosts) {
    root.RelatedPosts = factory();
  }
})(this, function() {
  "use strict";

  let _this = function RelatedPosts_constructor() {};

  let count = 0;                // フィード読み込み完了数
  let limit = 0;                // フィード読み込み予定数
  let urls = [];                // URL一覧
  let titles = [];              // タイトル一覧
  let current = '';             // 標的ページ
  let title = 'RelatedPosts';   // 関連記事タイトル
  let maxresults = 5;           // 最大関連記事数

  // 初期化
  _this.init = function RelatedPosts_init(currenturl, head, max) {
    current = currenturl;
    title = head;
    maxresults = max;
  };

  // フィード数分をカウントする
  _this.counter = function RelatedPosts_counter() {
    limit++;
  };

  // 関連記事を書き込む
  function RelatedPosts_write() {
    // ランダムインデックス生成
    let indexs = Array(urls.length);
    for (let i=0, r; i<indexs.length; i++) {
      r = Math.floor(Math.random() * (i+1));
      if (r !== i) indexs[i] = indexs[r];
      indexs[r] = i;
    }

    // 関連一覧の作成(重複要素は、出現しやすい)
    let dups  = [];
    let items = [];
    for (let i=0; i<indexs.length && dups.length<maxresults; i++) {
      if (dups.indexOf(urls[indexs[i]]) == -1) {
        dups.push(urls[indexs[i]]);
        items.push('<li><a href="' + urls[indexs[i]] + '">' + titles[indexs[i]] + '</a></li>');
      }
    }

    if (items.length > 0) {
      let line = [];
      line.push('<h4>' + title + '</h4>')
      line.push('<ul>');
      for (let i=0; i<items.length; i++) {
        line.push(items[i]);
      }
      line.push('</ul>');

      let posts = document.getElementById('related-posts');
      posts.insertAdjacentHTML('beforeend', line.join(''));
      posts.style.display = 'block';
    }
  }

  // フィードの要素を追加する
  _this.add = function RelatedPosts_add(json) {
    let m = current.match(/^https?:\/\/(.+$)/);
    let http  = '';
    let https = '';
    if (m != null) {
      http  = 'http://'+m[1];
      https = 'https://'+m[1];
    }
    for (let i=0; i<json.feed.entry.length; i++) {
      let entry = json.feed.entry[i];
      for (let k=0; k<entry.link.length; k++) {
        if (entry.link[k].rel == 'alternate') {
          if (!(http == entry.link[k].href || https == entry.link[k].href)) {
            if (0 != entry.thr$total.$t) {
              // コメントありの時
              // 出現確率を上げる
              titles.push(entry.title.$t);
              urls.push(entry.link[k].href);
            }
            titles.push(entry.title.$t);
            urls.push(entry.link[k].href);
            // サムネイル画像
            // Bloggerに画像が保存していることが必須
            // Googleフォト等で別に画像をロードしている場合、存在しない
            //if (entry.media$thumbnail) {  imgs.push(entry.media$thumbnail.url); }
            //else {                        imgs.push(''); }
          }
          break
        }
      }
    }
    count++;
    if (count == limit) {
      RelatedPosts_write();
      urls.splice(0, urls.length);
      titles.splice(0, titles.length);
    }
  };

  return _this;
});

template

<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <style type='text/css'>
  ... 上記のstyle
  </style>
  <script type='text/javascript'>
//<![CDATA[
  ... 上記のjs
//]]>
  </script>
  <div id='related-posts'>
    <script type='text/javascript'>RelatedPosts.init(&quot;<data:post.url/>&quot;, &quot;関連記事&quot;, 5);</script>
    <b:loop values='data:post.labels' var='label'>
      <script type='text/javascript'>RelatedPosts.counter();</script>
    </b:loop>
    <b:loop values='data:post.labels' var='label'>
      <script async='async' expr:src='&quot;/feeds/posts/summary/-/&quot; + data:label.name + &quot;?alt=json-in-script&amp;callback=RelatedPosts.add&amp;max-results=10&quot;' type='text/javascript'/>
    </b:loop>
  </div>
</b:if>

</article>の直前などに配置する。
RelatedPosts.initの引数でURL、タイトル、関連記事数を指定する。

参考