2014年6月4日水曜日

Blogger単独で関連記事を表示する方法 (Related Posts)

2014年6月4日水曜日 , , , , , , ,
Blogger単独で関連記事を表示する方法の模索をする (Related Posts) 
http://saskwave.blogspot.jp/2014/05/blogger-related-posts.html

この記事でBloggerにも外部サービスに依存しない関連記事ウィジェットを実装したいと書きましたが、実装しました。

テンプレートは弄らないで実装できないか検討しましたが、テンプレートを弄らないとそもそも個別記事のラベルを取得する方法がないので、結局テンプレートを弄ることにしました。そのかわり最小限の編集で済むような実装にしてあります。

この関連記事ウィジェットの特徴

  • jQueryライブラリsaskwave.jsを使ったシンプルな実装
  • コードはテンプレートへ追加するだけ
  • 関連記事の表示は即時反映されます
  • 表示件数を任意の数にしたり、関連記事ウィジェットのタイトルを編集できます
  • 基本的な表示の制御はCSS(スタイルシート)のみで行えるようなHTML構造
  • 見た目重視でサムネイル画像を全面採用 (記事に画像を掲載していない場合、代替画像が表示されます)
  • サムネイル画像は自動的に必要最低限の大きさにリサイズされるので、通信データ量を削減できる (通信が遅いモバイル端末に有利)
  • デフォルトで用意されている関連記事ウィジェットの表示方式は4種類 (見た目については下記を参照)
  • 記事のラベルから過去記事との関連度を測って記事を抽出するので、関連度が高い記事をリストアップできる (関連記事は【関連度+日付降順】に並びます)
  • 必要なコードだけあれば、外部サービスに依存しないので、Blogger単独で利用可能 (Blogger以外のサービスに影響されません)
たくさん書きましたが、こんなところです。
一番の利点は、外部サービスに影響しない点と、ちゃんと関連度順に並ぶという点ですね。
あと見た目も選べるのも利点です。

それはさておき、デフォルトで用意されている表示方式は下記の通りです。
4種類(tile|vertical|horizon|none)あり、選択するだけで見た目が変わります。
  1. tile
    関連記事をタイル状に配置します。
    一番表示領域を占有しますが、大きく配置されるので関連記事を目立たせることができます。
  2. vertical
    関連記事を垂直に配置します。
    他の表示方式と違って、表示領域を大きく占有しないスタイルです。
  3. horizon
    関連記事を水平に配置します。
    縦並びに配置されるので見やすいスタイルになります。
  4. none
    関連記事を非表示にします。
 サンプル画像を用意しましたので下記の項目を見てみてください。

デフォルトで用意されている表示方式

デフォルトで用意されている表示方式は4種類(tile|vertical|horizon|none)あります。

tile

とにかく目立たせられますが、一番表示領域を食います。

vertical

高さもなく、一番シンプルです。

horizon

見やすいですが、関連記事が多いと縦に表示領域を食います。

none

何も表示されません。

実装手順

長くなりましたが、それでは実装手順です。

まずは、jQueryライブラリsaskwave.js(saskwave.min.js)と関連記事スクリプト(saskwave.related-posts.min.js)が必要なので、下記からダウンロードしてください。ダウンロードが面倒くさい場合、GoogleのCDNと当方のファイル置場から直接リンクして使ってください(リンク切れの保証はしませんが…)。
ダウンロードしたら、それぞれのJavascriptファイルを自前のサーバーにアップロードしておきます。

Bloggerのテンプレートを開き、HTMLの編集をクリックします。
下の画像で赤く囲ってある部分です。


そうすると直接テンプレートがいじれるので、Ctrl+F<head>を探します。
その中にmeta要素があるので、一番下のmeta要素を探し、すぐ下の部分を改行してあけてやります。
下の画像でいうと、黄色で囲ってある部分です。


下記のHTMLコードをベースに、該当部分を自前のサーバーのJavascriptファイルのリンクに書き換えて、黄色の部分に貼り付けてください。書き換えるJavascriptファイルのリンクは、saskwave.min.jssaskwave.related-posts.min.jsの2つです。
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js' type='text/javascript'/>
<script src='http://googledrive.com/host/0ByEGaClBNxLRa3NFcm5UY2VQVkE/saskwave.min.js' type='text/javascript'/>
<script src='http://googledrive.com/host/0ByEGaClBNxLRa3NFcm5UY2VQVkE/saskwave.related-posts.min.js' type='text/javascript'/>

<!-- 下はsaskwave.jsで利用しているGoogle Feed APIです -->
<script src='https://www.google.com/jsapi' type='text/javascript'/>
<script type='text/javascript'>
  google.load('feeds', '1');
</script>
ダウンロードが面倒くさいという人は上記のHTMLコードをそのまま直接、黄色の部分に貼り付ければOKです。

次に関連記事ウィジェットのためのCSSコードを貼り付けます。
テンプレートでCtrl+Fをして、</head>を探します。


赤い線で示した部分(</head>の直上)を改行してあけて、下記のCSSコードを貼り付けます。
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <style type='text/css'>
    /* ----- COMMON ----- */
    #RelatedPosts .widget-content {
      margin: 30px 0 0;
    }
    
    #RelatedPosts .widget-content h2 {
      margin: 16px 0 15px;
      padding: 3px;
      border: 2px solid rgb(204, 0, 34);
      box-shadow: 0 1px 0 0 rgb(255, 255, 255);
      font-size: 20px;
      text-indent: 0.3em;
      text-shadow: 0 1px 2px rgba(0, 0, 0, .15);
      color: rgb(204, 0, 34);
    }
    
    #RelatedPosts .widget-content a:hover,
    #RelatedPosts .widget-content img:hover,
    #RelatedPosts .widget-content div.thumb:hover {
      opacity: 0.7;
      filter: alpha(opacity=70);
    }
    
    /* ----- TILE ----- */
    #RelatedPosts ul.tile {
      list-style: none;
      width: 700px; /* (320 * 2) + (10 * 4) + 20 = 700px */
      margin: 0 auto;
    }
    
    #RelatedPosts ul.tile li {
      display: inline;
      float: left;
      width: 320px;
      margin: 10px;
      padding: 0;
      border: 1px solid rgb(195, 195, 195);
    }
    
    #RelatedPosts ul.tile .item-content {
      margin: 0;
      padding: 0;
    }
    
    #RelatedPosts ul.tile .item-thumbnail {
      margin: 0;
      padding: 0;
    }
    
    #RelatedPosts ul.tile .item-thumbnail div.thumb {
      margin: 0;
      padding: 0;
    }
    
    #RelatedPosts ul.tile .item-date {
      padding: 5px;
    }
    
    #RelatedPosts ul.tile .item-title,
    #RelatedPosts ul.tile .item-title * {
      padding: 0 5px 5px;
      font-weight: bold;
      height: 5em;
    }
    
    #RelatedPosts ul.tile .item-snippet {
      display: none;
    }
    
    /* ----- VERTICAL ----- */
    #RelatedPosts ul.vertical {
      list-style: none;
      display: table;
      width: 100%;
      margin: 0;
      padding: 10px 0 0;
    }
    
    #RelatedPosts ul.vertical li {
      display: table-cell;
      text-align: center;
      width: 140px;
      border-right: 3px dotted rgb(195, 195, 195);
    }
    
    #RelatedPosts ul.vertical li:nth-last-of-type(1) {
      border-style: none;
    }
    
    #RelatedPosts ul.vertical .item-content {
      padding: 5px 20px 5px;
    }
    
    #RelatedPosts ul.vertical .item-thumbnail {
      display: inline-block;
    }
    
    #RelatedPosts ul.vertical .item-thumbnail div.thumb {
      border-radius: 5px 5px 5px 5px;
      -moz-border-radius: 5px 5px 5px 5px;
      -webkit-border-radius: 5px 5px 5px 5px;
    }
    
    #RelatedPosts ul.vertical .item-date {
      padding: 10px 0 5px;
      text-align: center;
    }
    
    #RelatedPosts ul.vertical .item-title,
    #RelatedPosts ul.vertical .item-title * {
      text-align: center;
      font-weight: bold;
      word-wrap: break-word;
    }
    
    #RelatedPosts ul.vertical .item-snippet {
      display: none;
    }
    
    /* ----- HORIZON ----- */
    #RelatedPosts ul.horizon {
      list-style: none;
      margin: 0;
      padding: 10px 15px 0;
    }
    
    #RelatedPosts ul.horizon li {
      margin: 2px 0 2px;
      padding-bottom: 10px;
      border-bottom: 3px dotted rgb(195, 195, 195);
    }
    
    #RelatedPosts ul.horizon li:nth-of-type(1) {
      padding-top: 0 !important;
    }
    
    #RelatedPosts ul.horizon li:nth-last-of-type(1) {
      padding-bottom: 0 !important;
      border-style: none;
    }
    
    #RelatedPosts ul.horizon .item-content {
      margin-top: 3px;
    }
    
    #RelatedPosts ul.horizon .item-thumbnail {
      float: left !important;
      margin: 0 10px 0 0 !important;
    }
    
    #RelatedPosts ul.horizon .item-thumbnail div.thumb {
      margin: 1px 3px 0 0;
      padding: 0;
      border-radius: 5px 5px 5px 5px;
      -moz-border-radius: 5px 5px 5px 5px;
      -webkit-border-radius: 5px 5px 5px 5px;
    }
    
    #RelatedPosts ul.horizon .item-date {
      display: block;
      font-size: 14px;
      color: rgb(120, 120, 120);
    }
    
    #RelatedPosts ul.horizon .item-title,
    #RelatedPosts ul.horizon .item-title * {
      padding: 5px 0;
      font-size: 16px;
      font-weight: bold;
    }
    
    #RelatedPosts ul.horizon .item-snippet {
      font-size: 14px;
      line-height: 120%;
      height: 3.6em; /* (1.2 * 3) = 3.6em */
      overflow: hidden;
    }

    /* This hack uses webkit specific feature.
     This will be used only in google chrome. */
    @media screen and (-webkit-min-device-pixel-ratio:0) {
      #RelatedPosts ul.horizon .item-snippet {
        line-height: 120%;
        height: 3.5em; /* (1.2 * 3) = 3.6em - α */
        overflow: hidden;
      }
    }
  </style>
</b:if>
このCSSでは、3種類(tile|vertical|horizon)が定義されています(noneはスタイル不要)。

次に関連記事ウィジェットのためのテンプレートコードを貼り付けます。
テンプレートでCtrl+Fをして、<b:if cond='data:post.hasJumpLink'>を探します。
<div class='post-footer'>を探してもOKですが、モバイル用とPC用の2つあるので注意が必要です(PC用を探してください)。


<div class='post-footer'>の真上、上の画像だと赤い線の部分に、下記のテンプレートコードを貼り付けてください。
<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <script type='text/javascript'>
  //<![CDATA[
    var RelatedPostsConfig = {
      /* ------ OPTIONS -------- */
      widgetTitle: '関連性が高い記事はこちら',
      showResults: 4,
      styleMode  : 'tile', // tile|vertical|horizon|none
      
      /* ------ NO IMAGE ------- */
      noImage100x100: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAGFBMVEXMzMzLy8v////Hx8fU1NTn5+fz8/Pb29tgAqqLAAABY0lEQVRYw+2SzXKDIBCAofYBIPgAZv3JNUA116o0uWMcr+06eYBqMnn9os2hEzM9tzN8syIz8A3sLsTj8Xg8Ho/nz8DZHJq7OeWUu4HNH6FaugXO7w16oFO8tKPbFH6yM+NXScTVqfLSSXq4ntmdEthYYMkAgBKi4h3IEDgJYSRiAEgEQrZdKKnGUqXGrhkp4hxKF6SAnu3g+JZp7LrlKRBgbRuep1unFNAoGOkqW3MVSVEL1JosFNtjDe76IJ2isrjCJhi6VFYN1VojpMuL7Td2VnBSVqfIVr1AAXpo2KltHioj/Dilet+A6kNooa7cD3oUmiyVAVwu+juXoYFUuSKAyymRAY4YyAdKAaXKzjhXzI42KSIVmafYVWzvKtZ2bKkwKDne+oJ1FefJ0PN8oweAj0d9oc80MFP366n7teFhyV4NJcxwfTlKaoxhv78xzjibRjclVNzemMfj8Xg8Hs8/5gtAeU6oiwTZAgAAAABJRU5ErkJggg==',
      noImage320x150: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAACWBAMAAABdrkwfAAAAD1BMVEXMzMz////m5ub09PTY2Ng4o268AAADgUlEQVR42u2XXXajMAxGk8ACMGYB4HYB0HQBdJr9r2mwJKRoyJyTxu7bdx9aY2z5Gv/mBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/z20tD3GrIdKm9M6pS0oq9RZCiKM86GvKSClJzh/K0dcSx0JsfKzaCvPzbl+2MFzrK4RZwi2BcIbnELr8jsoTE+UQVDtQwoeIq7QizC8JziqoLauhE9TyxJYa9PUk4vaohuWCvRf8DjvxsWDHH8kJausSQukrCAYn2AZjPgg2omVdkwcRV3dlrCA4k6D+37gmmuTxIHjWXEuxt+hqiJjSwrnlgsO9YI76vs/z8Si4oRbR3mq+hPjch3rmVvpb5pVVnLkTvIiwpUxw/zirrgO/KFY3rzlopBzNeFFwNsEzNyTK8V/BSQdKlBgbQA3BLHMVwcEEJ3rye6QX5ESrgvJg2+J0v3pOVQSjCd5PvIWynOBC/dGKo66ReFYJylfKBSMFFMFWJxY7d48Eoy6LUV8OF86XEDUF+2ULL4I+VEMvTJC/zlac5Zf7s6fTQ7DxNuUbdb+1HVnQOZmtCWaLLxKZKNHpXBj5j3SrrmCuPpIge7jxPwjGMxXkhJ16q14eztUFc9+7R4LtQTAXb3Kq5cSgHRExKVlXkDa85wXbTSQnOhWkBUxL2QkGoViQN7ynBMkp5+Zx1in6pfPz/4LxmhlfE6QxfmKR8DDm0tlp1Cm6uC1UQphg8TbDh9oDweYoKLN1nUKwD5wv9ykl9pRaJhgrCDZ8VM1u4dq0N8HcCxrerRgZuCWgZ0ysLWiHvWy3tv96wdwL+spUTQQbd3+2o+66sVBeueC0C9r5ay15wVG+WEdlV1ojhkzJwZ+W5YKNCk4Wq/G3GVPiwqzLVQQVjv6+US4obYrVKMGtIS+40D1BBU3PQny6G1u5oF1EW/6tKCPXOUFy0cv0/QXoRkx2VZRO0hSsItiQ4K4ab6f2jUbMCVLT9ntJJphtRo32ln/X3BbuY/lGLb2eOccYTgfBKL3p/eatZ7L8N9YKZ/He61lSyuoFqbS01+mZMVm7e5XJ9bGOYKPVLy74QVBEZhF0m9Fy/PG/1hJsrfq3HQFekIxEZJTKVNHtehZC1csFrfcWvl8PgjTdeAh1zjX+ytNrXF4pB8EqtG/Xj/fCEOl6TesJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH6Pv7p3oZRrXHSJAAAAAElFTkSuQmCC',
      
      /* ------ FOR DISPLAY ---- */
      containerId: '#RelatedPosts'
    };
  //]]>
  </script>
  <div id='RelatedPosts'>
    <script type='text/javascript'>
      var _RP_LFS_ = $(RelatedPostsConfig.containerId);
      var _RP_LN_ = null;
      var _RP_LFU_ = null;
    </script>
    <b:loop values='data:post.labels' var='label'>
      <b:if cond='data:label.isLast != &quot;true&quot;'/>
      <script type='text/javascript'>
        _RP_LN_ = '<data:label.name/>';
      </script>
      <script type='text/javascript'>
      //<![CDATA[
        Saskwave.RelatedPostsWidget.collectLabels(_RP_LN_);
        _RP_LFU_ = '/feeds/posts/default/-/' + encodeURIComponent(_RP_LN_) + '?alt=json-in-script&callback=Saskwave.RelatedPostsWidget.collectPosts&max-results=100';
        _RP_LFS_.append($(Saskwave.createNode('script')).attr({ type: 'text/javascript', src: _RP_LFU_ }));
        console.log('Feed loading: ' + _RP_LFU_);
      //]]>
      </script>
    </b:loop>
    <script type='text/javascript'>
      Saskwave.RelatedPostsWidget.removeDuplicates('<data:post.url/>');
      Saskwave.RelatedPostsWidget.sort();
      Saskwave.RelatedPostsWidget.show(RelatedPostsConfig.containerId);
    </script>
  </div>
  <div style='clear:both;'/>
</b:if>

なお、上記コードの下記の部分を編集すると、関連記事ウィジェットを変更できます。
var RelatedPostsConfig = {
  /* ------ OPTIONS -------- */
  widgetTitle: '関連性が高い記事はこちら',
  showResults: 4,
  styleMode  : 'tile', // tile|vertical|horizon|none
   
  /* ------ NO IMAGE ------- */
  noImage100x100: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAGFBMVEXMzMzLy8v////Hx8fU1NTn5+fz8/Pb29tgAqqLAAABY0lEQVRYw+2SzXKDIBCAofYBIPgAZv3JNUA116o0uWMcr+06eYBqMnn9os2hEzM9tzN8syIz8A3sLsTj8Xg8Ho/nz8DZHJq7OeWUu4HNH6FaugXO7w16oFO8tKPbFH6yM+NXScTVqfLSSXq4ntmdEthYYMkAgBKi4h3IEDgJYSRiAEgEQrZdKKnGUqXGrhkp4hxKF6SAnu3g+JZp7LrlKRBgbRuep1unFNAoGOkqW3MVSVEL1JosFNtjDe76IJ2isrjCJhi6VFYN1VojpMuL7Td2VnBSVqfIVr1AAXpo2KltHioj/Dilet+A6kNooa7cD3oUmiyVAVwu+juXoYFUuSKAyymRAY4YyAdKAaXKzjhXzI42KSIVmafYVWzvKtZ2bKkwKDne+oJ1FefJ0PN8oweAj0d9oc80MFP366n7teFhyV4NJcxwfTlKaoxhv78xzjibRjclVNzemMfj8Xg8Hs8/5gtAeU6oiwTZAgAAAABJRU5ErkJggg==',
  noImage320x150: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAACWBAMAAABdrkwfAAAAD1BMVEXMzMz////m5ub09PTY2Ng4o268AAADgUlEQVR42u2XXXajMAxGk8ACMGYB4HYB0HQBdJr9r2mwJKRoyJyTxu7bdx9aY2z5Gv/mBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/z20tD3GrIdKm9M6pS0oq9RZCiKM86GvKSClJzh/K0dcSx0JsfKzaCvPzbl+2MFzrK4RZwi2BcIbnELr8jsoTE+UQVDtQwoeIq7QizC8JziqoLauhE9TyxJYa9PUk4vaohuWCvRf8DjvxsWDHH8kJausSQukrCAYn2AZjPgg2omVdkwcRV3dlrCA4k6D+37gmmuTxIHjWXEuxt+hqiJjSwrnlgsO9YI76vs/z8Si4oRbR3mq+hPjch3rmVvpb5pVVnLkTvIiwpUxw/zirrgO/KFY3rzlopBzNeFFwNsEzNyTK8V/BSQdKlBgbQA3BLHMVwcEEJ3rye6QX5ESrgvJg2+J0v3pOVQSjCd5PvIWynOBC/dGKo66ReFYJylfKBSMFFMFWJxY7d48Eoy6LUV8OF86XEDUF+2ULL4I+VEMvTJC/zlac5Zf7s6fTQ7DxNuUbdb+1HVnQOZmtCWaLLxKZKNHpXBj5j3SrrmCuPpIge7jxPwjGMxXkhJ16q14eztUFc9+7R4LtQTAXb3Kq5cSgHRExKVlXkDa85wXbTSQnOhWkBUxL2QkGoViQN7ynBMkp5+Zx1in6pfPz/4LxmhlfE6QxfmKR8DDm0tlp1Cm6uC1UQphg8TbDh9oDweYoKLN1nUKwD5wv9ykl9pRaJhgrCDZ8VM1u4dq0N8HcCxrerRgZuCWgZ0ysLWiHvWy3tv96wdwL+spUTQQbd3+2o+66sVBeueC0C9r5ay15wVG+WEdlV1ojhkzJwZ+W5YKNCk4Wq/G3GVPiwqzLVQQVjv6+US4obYrVKMGtIS+40D1BBU3PQny6G1u5oF1EW/6tKCPXOUFy0cv0/QXoRkx2VZRO0hSsItiQ4K4ab6f2jUbMCVLT9ntJJphtRo32ln/X3BbuY/lGLb2eOccYTgfBKL3p/eatZ7L8N9YKZ/He61lSyuoFqbS01+mZMVm7e5XJ9bGOYKPVLy74QVBEZhF0m9Fy/PG/1hJsrfq3HQFekIxEZJTKVNHtehZC1csFrfcWvl8PgjTdeAh1zjX+ytNrXF4pB8EqtG/Xj/fCEOl6TesJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH6Pv7p3oZRrXHSJAAAAAElFTkSuQmCC',
   
  /* ------ FOR DISPLAY ---- */
  containerId: '#RelatedPosts'
};
簡単に説明すると、下記の通りです。
  • widgetTitle
    関連記事ウィジェットのタイトルです。
    '関連記事'と書き換えると、そのように表示されます。
    なお、文字列は必ず'(シングルクォート)で囲ってください。
  • showResults
    関連記事を表示する数を指定します。
    通常は3-10の間で指定するときれいに収まるかと思います。
    なお、styleMode = 'tile';を指定する場合、showResults偶数を指定すると収まりがよくなります。
  • styleMode
    表示方式を指定します。
    tile
    verticalhorizonnoneを指定できます。
    それぞれの文字列は必ず'(シングルクォート)で囲ってください(tileだったら’tile’です)。
  • noImage***x***
    カスタム画像を設定することができます。
  • containerId
    関連記事ウィジェットを表示する場所を示すコンテナIDです。
    通常は弄らないでください。
以上でテンプレートの編集作業は終わりです!
あとはテンプレートを保存をクリックしてテンプレートを保存しましょう。


これで一連の作業は終わりました。
実際に自分のブログを確認して、正しく関連記事ウィジェットが表示されているか確認しましょう。

最後に

関連度について画像で説明します。


画像の説明通り、関連度順に記事が並んでますね。
この画像では分かりませんが、同じ関連度の記事が2つ以上ある場合、その並びは日付降順になります。
なお、マウスオーバーした時に出るRelation Levelで関連度(どれだけ個別記事のラベルと一致するか)が数値としてわかります。

改訂

2014/06/12
  • コードの手直しをしました。
  • ラベルをフィードとして扱う際に、encodeURIComponentしてないバグを修正しました。
  • 一部のscript要素をCDATAセクションとしました(重要です)。

0 件のコメント:

コメントを投稿