Bloggerテンプレート自作 #13:関連記事を設置する

前回の「個別記事ページにページャを設置する」に引き続き、今回は「関連記事を設置する」を実践してみようと思う。

Bloggerには標準で関連記事を表示させる関数やガジェットは用意されていない。なので、これが設置したければJavaScriptなんかで独自に実装する必要がある。自力で実装するのは結構骨が折れるので、今回は他所様のコードをお借りして実装することにした。非常に便利なコードを見つけたので、今回はその実装方法をまとめておくことにする。


関連記事のコードについて


関連記事コードのソース先



今回実装するコードは、上記のITブロガー雑記さんが公開されているコードを引用させてもらったもの。すでに使用しているテンプレートに関連記事を設定したい場合は、リンク先の方法を参考にコードをコピペすれば実装できるようになっている。また、デザインもいくつか公開されているので、その中から好きなものを選んで実装することができる。

なお、元々のソースは下記のサイト様だそうです。


ちなみに、この関連記事コードはVaster2でも採用されているっぽい。なので、Vaster2ユーザーなら、おおよその仕様や使い勝手は分かっているものと思われる。つまり、今回はVaster2の関連記事を実装するというのが主旨になっている。

関連記事コードの内容


※下記のコードはテンプレート制作用に加工しているので、現行のテンプレートに関連記事を表示させたい場合は「ITブロガー雑記」さんの該当記事から実装方法を参照してください

コード1:<head>タグ内に記述するコード


<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <script type='text/javascript'>
  //<![CDATA[
  imgr=new Array();imgr[0]="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGt1yRvpdflZqiBxOcxRJS4kbi4RNpICupF9-nY40pwa26l124rufPfCou_mvP1Df9XhjvmPoiPVEs9E-r2CW0b2cGgWgakhGAM71qRs0T23Qwzf27HcyDitJWxvxzU-9gWorYv4WN0gA/w400/";showRandomImg=true;aBold=true;summaryPost=400;summaryTitle=20;numposts1=12;numposts2=4;function removeHtmlTag(strx,chop){var s=strx.split("<");for(var i=0;i<s.length;i++){if(s[i].indexOf(">")!=-1){s[i]=s[i].substring(s[i].indexOf(">")+1,s[i].length)}}s=s.join("");s=s.substring(0,chop-1);return s}
  function showrecentposts1(json){j=(showRandomImg)?Math.floor((imgr.length+1)*Math.random()):0;img=new Array();if(numposts2<=json.feed.entry.length){maxpost=numposts2}else{maxpost=json.feed.entry.length}for(var i=0;i<maxpost;i++){var entry=json.feed.entry[i];var posttitle=entry.title.$t;var pcm;var posturl;if(i==json.feed.entry.length)break;for(var k=0;k<entry.link.length;k++){if(entry.link[k].rel=='alternate'){posturl=entry.link[k].href;break}}for(var k=0;k<entry.link.length;k++){if(entry.link[k].rel=='replies'&&entry.link[k].type=='text/html'){pcm=entry.link[k].title.split(" ")[0];break}}if("content"in entry){var postcontent=entry.content.$t}else if("summary"in entry){var postcontent=entry.summary.$t}else var postcontent="";postdate=entry.published.$t;if(j>imgr.length-1)j=0;img[i]=imgr[j];s=postcontent;a=s.indexOf("<img");b=s.indexOf("src=\"",a);c=s.indexOf("\"",b+5);d=s.substr(b+5,c-b-5);if((a!=-1)&&(b!=-1)&&(c!=-1)&&(d!=""))img[i]=d;var month=[1,2,3,4,5,6,7,8,9,10,11,12];var month2=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];var day=postdate.split("-")[2].substring(0,2);var m=postdate.split("-")[1];var y=postdate.split("-")[0];for(var u2=0;u2<month.length;u2++){if(parseInt(m)==month[u2]){m=month2[u2];break}}var daystr=day+' '+m+' '+y;pcm='<a href="'+posturl+'">'+pcm+' comments</a>';var trtd='<div class="col_maskolis"><h2 class="posttitle"><a href="'+posturl+'">'+posttitle+'</a></h2><a href="'+posturl+'"><img class="related-img" src="'+img[i]+'"/></a><div class="clear"></div></div>';document.write(trtd);j++}}var relatedTitles=new Array();var relatedTitlesNum=0;var relatedUrls=new Array();var thumburl=new Array();function related_results_labels_thumbs(json){for(var i=0;i<json.feed.entry.length;i++){var entry=json.feed.entry[i];relatedTitles[relatedTitlesNum]=entry.title.$t;try{thumburl[relatedTitlesNum]=entry.gform_foot.url}catch(error){s=entry.content.$t;a=s.indexOf("<img");b=s.indexOf("src=\"",a);c=s.indexOf("\"",b+5);d=s.substr(b+5,c-b-5);if((a!=-1)&&(b!=-1)&&(c!=-1)&&(d!="")){thumburl[relatedTitlesNum]=d}else thumburl[relatedTitlesNum]='https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGt1yRvpdflZqiBxOcxRJS4kbi4RNpICupF9-nY40pwa26l124rufPfCou_mvP1Df9XhjvmPoiPVEs9E-r2CW0b2cGgWgakhGAM71qRs0T23Qwzf27HcyDitJWxvxzU-9gWorYv4WN0gA/w400/'}if(relatedTitles[relatedTitlesNum].length>35)relatedTitles[relatedTitlesNum]=relatedTitles[relatedTitlesNum].substring(0,35)+"...";for(var k=0;k<entry.link.length;k++){if(entry.link[k].rel=='alternate'){relatedUrls[relatedTitlesNum]=entry.link[k].href;relatedTitlesNum++}}}}function removeRelatedDuplicates_thumbs(){var tmp=new Array(0);var tmp2=new Array(0);var tmp3=new Array(0);for(var i=0;i<relatedUrls.length;i++){if(!contains_thumbs(tmp,relatedUrls[i])){tmp.length+=1;tmp[tmp.length-1]=relatedUrls[i];tmp2.length+=1;tmp3.length+=1;tmp2[tmp2.length-1]=relatedTitles[i];tmp3[tmp3.length-1]=thumburl[i]}}relatedTitles=tmp2;relatedUrls=tmp;thumburl=tmp3}function contains_thumbs(a,e){for(var j=0;j<a.length;j++)if(a[j]==e)return true;return false}function printRelatedLabels_thumbs(){for(var i=0;i<relatedUrls.length;i++){if((relatedUrls[i]==currentposturl)||(!(relatedTitles[i]))){relatedUrls.splice(i,1);relatedTitles.splice(i,1);thumburl.splice(i,1);i--}}var r=Math.floor((relatedTitles.length-1)*Math.random());var i=0;if(relatedTitles.length>0)document.write('<div class="related-text">'+relatedpoststitle+'</div>');document.write('<div class="p-container"/>');while(i<relatedTitles.length&&i<20&&i<maxresults){document.write('<a style="text-decoration:none;');if(i!=0)document.write('"');else document.write('"');document.write(' href="'+relatedUrls[r]+'"><div class="related-img" style="background-image: url('+thumburl[r]+');"></div><div class="related-title">'+relatedTitles[r]+'</div></a>');if(r<relatedTitles.length-1){r++}else{r=0}i++}document.write('</div>');relatedUrls.splice(0,relatedUrls.length);thumburl.splice(0,thumburl.length);relatedTitles.splice(0,relatedTitles.length)}
  //]]>
  </script>
</b:if>

上記が関連記事の動作を司るJavaScriptのコードとなる。JSのコードは</body>の上というのが相場だが、このコードは<head>タグ内に設置したければ正しく動作しない。また、このコードの冒頭にある条件分岐は個別記事ページのみで読み込まれるようにするもの(設定しないとJSコードがすべてのページで読み込まれるため、読み込みがやや遅くなる)。

このコードには 画像なしの記事 が選ばれた場合に代替サムネイルが表示される仕様になっている。代替サムネイルのURLは「https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGt1yRvpdflZqiBxOcxRJS4kbi4RNpICupF9-nY40pwa26l124rufPfCou_mvP1Df9XhjvmPoiPVEs9E-r2CW0b2cGgWgakhGAM71qRs0T23Qwzf27HcyDitJWxvxzU-9gWorYv4WN0gA/w400/」となる。なので、代替サムネイルを変更した場合は、この部分を別の画像URLで上書きすれば良い。

なお、「ITブロガー雑記」さんでは<style>タグを含めたコードを紹介されているが、これはコピペのみをする人向けの設定なので、CSSで再度デザインをし直す場合には不要となる。というわけで、自分はこれをCSS記述部分に移植し、元のコードから<style>タグの一切を削除した。

コード2:<body>タグ内に記述するコード


<b:if cond='data:blog.pageType == &quot;item&quot;'>
  <div id='related-posts'>
    <b:loop values='data:post.labels' var='label'>
      <b:if cond='data:label.isLast != &quot;true&quot;'>
      </b:if>
      <b:if cond='data:blog.pageType == &quot;item&quot;'>
        <script expr:src='&quot;/feeds/posts/default/-/&quot; + data:label.name + &quot;?alt=json-in-script&amp;callback=related_results_labels_thumbs&amp;max-results=99&quot;' type='text/javascript'/>
      </b:if>
    </b:loop>
    <script type='text/javascript'>
      var currentposturl=&quot;<data:post.url/>&quot;;
      var maxresults=8; // 最大表示件数
      var relatedpoststitle=&quot;関連記事&quot;; // 見出しに表示される文字列
      removeRelatedDuplicates_thumbs();
      printRelatedLabels_thumbs();
    </script>
  </div>
</b:if>

上記がページ上で関連記事を表示させるコードになる。

「ITブロガー雑記」さんでは条件分岐で個別記事ページのみで読み込まれる設定にしてあるが、テンプレートに組み込む場合は個別記事の処理内に記述することになるので基本的には不要。あっても問題はない。

コードの下部のJS部分は、関連記事の見出しや記事表示数をコントロールできるようするものである。

なお、「ITブロガー雑記」さんでは「HTMLの編集」からBloggerエディタを開いて、そこにコードをコピペするように説明されている。「ITブロガー雑記」さんで紹介されているコードをその方法でコピペした場合、BloggerエディタがHTMLを自動で変換するためにエラーは起きないが、直接xmlを編集してアップロードする場合はエラーが出る。

その理由は、元のコードが <b:if cond='data:blog.pageType == "item"'> のようにXML用の記述になっていないからであり、これをxml用にするには <b:if cond='data:blog.pageType == &quot;item&quot;'> のように " を &quot; にするなど、該当するHTML部分をxml用に置き換えなければならない(該当するのは " = &quot; と & = &amp; である)。

上記のコードはxml用に書き直しているので、xmlに直接書き込む場合は上記のコードをコピペすればOK。

追記:最大表示件数通りに表示されないバグの対処法


自作テンプレートのテスト中に、関連記事の最大表示件数が機能していなことに気づいた。そこで、色々と試してみて原因を究明してみると、以下のコードが原因だということが分かった。

<script expr:src='&quot;/feeds/posts/default/-/&quot; + data:label.name + &quot;?alt=json-in-script&amp;callback=related_results_labels_thumbs&amp;max-results=8&quot;' type='text/javascript'/>

どうやら、最大表示件数の上部にある このコードの「max-results=8」がJSで定められた真の最大表示件数らしい。

<script expr:src='&quot;/feeds/posts/default/-/&quot; + data:label.name + &quot;?alt=json-in-script&amp;callback=related_results_labels_thumbs&amp;max-results=99&quot;' type='text/javascript'/>

そこで、上記のように「max-results=99」と書き換えてから、最大表示件数をいじると上手く表示されるようになった。


コード3:CSS


Bloggerテンプレート自作 #13:関連記事を設置する

/* ----------------------------------------------------------------------------
    関連記事
---------------------------------------------------------------------------- */

/* 見出し */

.related-text {
  font-size: 1.5em;
  font-weight: bold;
  margin: 2em 0 1em;
  border-bottom: 2px solid #444;
}

/* 関連記事表示部分 */

.p-container {
  display: flex;
  flex-wrap: wrap;
}

/* 関連記事のリンク設定 */

#related-posts a {
  display: block;
  width: 24%;
  color: #444;
  margin: 0 1% 2em 0;
  transition: all 0.3s ease;
}

#related-posts a:hover {
  opacity: 0.7;
}

/* 関連記事の画像設定 */

.related-img {
  width: 100%;
  height: 110px;
  background-size: cover;
  background-position: center;
}

/* 関連記事の記事タイトル設定 */

.related-title {
  font-size: 90%;
  margin-top: 10px;
}

/* 関連記事のレスポンシブ設定 */

@media screen and (max-width: 600px) {
  .p-container {
    justify-content: space-between;
  }
  #related-posts a {
    width: 48%;
    margin-right: 0;
  }
}

上記が元のコードの関連記事のスタイル設定である。

これをCSS記述欄にコピペすれば、関連記事部分の装飾をすることができる。

作業工程


コードを書き直して必要箇所にコピペする


関連記事を読み込むために以下のような作業を行った。

・コード1を</head>の上にコピペ
・コード2を個別記事の処理内にコピペ
・コード2を個別記事内に呼び出す処理を記述
・コード3をCSS記述部分にコピペ

コード2の記述場所は以下のようになる。

<!-- 個別記事ページ -->
<b:includable id='single-post'>
  <!-- 記事部分の記述(省略) -->
  <!-- ページャを読み込む記述(省略) -->
  <!-- 関連記事を読み込む -->
    <b:if cond='data:post.labels'> <!-- タグがあるがどうか -->
      <b:include name='single-related-posts' />
    <b:else/>
      <!-- タグがなければ非表示 -->
    </b:if>
</b:includable>

<!-- 関連記事 -->
<b:includable id='single-related-posts'>
  <!-- コード2の記述(省略) -->
</b:includable>

ちなみにタグなし記事で関連記事を読み込むと、何も無いスペースが出来てしまう。これを防ぐためにタグの有無を条件分岐で判定させ、もしタグがなければ何も読み込まない設定にしている。

CSSを書き直す


Bloggerテンプレート自作 #13:関連記事を設置する
PC表示

Bloggerテンプレート自作 #13:関連記事を設置する
スマホ表示

/* ----------------------------------------------------------------------------
  関連記事
---------------------------------------------------------------------------- */

/* 関連記事全体 */

#related-posts {
  background: #fff;
}

/* 見出し */

.related-text {
  font-size: 18px;
  font-weight: bold;
  letter-spacing: 2px;
  background: #333;
  color: #fff;
  padding: 6px 15px;
  margin: 0;
}

/* 関連記事表示部分 */

.p-container {
  display: flex;
  flex-wrap: wrap;
  padding: 20px 10px 10px;
}

/* 関連記事のリンク設定 */

#related-posts a {
  display: block;
  width: 24%;
  color: #444;
  margin: 0 1% 2em 0;
}

#related-posts a:hover {
  opacity: 0.6;
}

/* 関連記事の画像設定 */

.related-img {
  width: 100%;
  height: 110px;
  border: solid 1px #ddd;
  background-size: cover;
  background-position: center;
}

/* 関連記事の記事タイトル設定 */

.related-title {
  font-size: 90%;
  margin-top: 10px;
  font-weight: bold;
}

/* 関連記事のレスポンシブ設定 */

@media screen and (max-width: 560px) {
  .p-container {
    flex-direction: column;
    padding: 10px;
  }
  #related-posts a {
    display: flex;
    align-items: top;
    width: 94%;
    color: #444;
    padding: 0.8em;
    margin: 0;
    border-bottom: 1px solid #eee;
  }
  .related-img {
    width: 100px;
    height: 100px;
    margin-right: 15px;
  }
  .related-title {
    font-size: 14px;
    margin-top: 0;
  }
}

上記が書き直したCSS。製作中のテンプレートに合わせたデザインにした。

レスポンシブで、PC表示はヨコ並び、スマホ表示はタテ並びになるようにした。

余白が多いが、これは長い記事タイトルに対応させるためである。