2018-08-07T01:04:51Z

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

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、タイトル、関連記事数を指定する。

参考

 コメント: 1

まな さんのコメント... 

はじめまして。
Bloggerで関連記事表示する方法を探していて、こちらのページにたどり着きました。
素晴らしいコードを公開してくださってどうもありがとうございます!
おかげで私のブログにも無事関連記事を表示することができました!

 コメントを書く