読者です 読者をやめる 読者になる 読者になる

exfreeterのブログ

一念発起した。今日から頑張る。元ニートでもやれる。

ブログに「あわせて読みたい」を実装した

ブログ プログラミング JavaScript

(2014/11/21 追記)
この記事の内容は古いです。
SEOスパムになる恐れがあるということなので、記事内のコードをコピペしないようにして下さい。
詳しくは、本家・池田仮名さんの記事を参考にして下さい。

当ブログで配布していた「あわせて読みたいウィジット」がSEOスパムになっている可能性があるため差し替えをお願いします - 太陽がまぶしかったから

(ここまで追記)

久しぶりのプログラミングネタ。
といっても、そんな大した話ではないですが。

今回の記事から「あわせて読みたい」を表示することにしたので、それについて軽く紹介します。

LinkWithinは気に入っていたけど

1週間ほど前に、以下のような記事を書きました。

零細ブログにとって、はてブの人気・新着ウィジェットは意味がない - exfreeterのブログ零細ブログにとって、はてブの人気・新着ウィジェットは意味がない - exfreeterのブログ

もうタイトルが言いたいことの全てなんですが。訪問別ページビューを増やしたいブログをやってる以上は、多くの人に見てもらいたいって思うのが悲しい人間の性で。数日前に...

その際、id:kyokucho1989さんがコメントで「LinkWithin」を教えて下さり、ちょっと触ってみたところ良さそうだったため、これまで使っていました。

LinkWithin - Related Posts with Thumbnails

凄くシンプルで良いサービスなのですが、LinkWithinには1つ問題がありまして。
ご存知の方も多いと思いますが、スマホで表示すると画面が横スクロールしてしまうんですよね...。

一応僕もプログラマの端くれなんで、最初は自力で修正してやろうと思ったんですが、はてなブログのスマホのCSSをイジるのが面倒そうで。
僕自身がHTML/CSS/JavaScriptを書くのが嫌いなのもあって、いったんは「もう良いかな」っていう気持ちになったんです。

が、今日になって急に気になってきまして、LinkWithinの修正ではなく他の手段で関連記事を表示してみることにしました。

他の方のブログを色々見てみた

はてなブログのカスタマイズ方法や、関連記事の表示方法についてググってみて、たくさんの方のブログを参考にさせて頂きました。

その中で、

【スマホ対応】【はてなブログ用】ぼくのかんがえたさいきょうの「あわせて読みたい」ウィジットを作ってみた - 情報学の情緒的な私試論β【スマホ対応】【はてなブログ用】ぼくのかんがえたさいきょうの「あわせて読みたい」ウィジットを作ってみた - 情報学の情緒的な私試論β

ぼくのかんがえたさいきょうの「あわせて読みたい」前回は『はてなブログ...

という記事を見つけまして。
何度もホッテントリで見た有名なブログだし、この記事も以前に見たような記憶があるんですが、とにかく、

スマートフォン表示に対応しており、横スクロールにもなりません→LinkWithinのひと!

とあり、まさにタイムリー!
ただ人気記事を出すのではなく最新記事とランダムで混ぜ合わせるという発想も凄く面白いなと思いまして、有り難く参考にさせて頂くことにしました。
ありがとうございます!
というか、id:bulldraさん、めちゃくちゃ広い分野について書いていて、凄いですね...。

ちょっと自分流に改造

素晴らしいウィジェットだと思ったのですが、以下の点を改造させて頂きました。

  1. 完全ランダムではなく、はてブ数上位幾つかは必ず表示するようにした
  2. 最新記事を必ず表示するという部分を外した
  3. 「あわせて読みたい」という部分は記事取得が終わる前から表示しておき、その下に「記事を読込中...」と表示することにした
  4. Tweet数も表示するようにした
  5. 色やサイズ等のデザインを少しいじった


1については、僕みたいに1つだけ偶然に突出してブクマされた記事がある場合に、それを確実に表示したいな〜という下心です...。

2については、上位記事を必ず表示する関係上、最新記事も必須ということにすると、表示する記事の大半が固定されてしまうと思い、外させていただきました。

3については、元のコードでは記事取得の通信中に「Now Loading...」が表示される感じだったのですが、何も知らない読者の方の気持ちになって画面を確認したら「え?何をLoadしてるの?」と思っちゃいまして...。
なので、あらかじめ「あわせて読みたい」という表示を出しておき、その下に「記事を読込中...」とすることで、読んで下さる方にも何を読み込んでるのかが伝わるかな〜と...。

4と5については趣味です。

デザイン(CSS)については、以下の2つのブログを参考にさせて頂きました。

マトリョーシカ的日常

pixyzehn blog

特にスマホについては、マトリョーシカ的日常さんのを激しく参考にさせて頂きました。
ありがとうございます。

Google Ajax Feed APIについては以下のサイトが参考になるかも。

Google AJAX Feed API入門

あとは、http://b.hatena.ne.jp/entrylistsort=count&mode=rss&url=BLOG_URLを実際に叩いてレスポンスを確認しながらという感じでしょうか。

コード

(12/27 06:30追記 以下のコードに誤りがあったため修正しました。指定した個数分のはてブ数上位記事を配列に入れるfor文の中で、現在表示されている記事を弾く処理を入れ忘れていました。)

今回のコードを貼っておきます。(ほとんどid:bulldraさんのままですが...)
これを「記事下」に置いている感じです。
scpiptタグの中のpopularNUMの値が「必ず表示したい人気記事の数」になっています。
2にすると「はてブ数上位2つまでは必ず表示される」、3にすると「上位3つまでは必ず表示される」という具合です。(1だと最上位の記事のみ)

<style>
.entry-list #new-entries-title {
background: #000;
color: #fff;
padding: 10px 10px 10px 10px;
margin: 15px 0 10px 0;
position: relative;
font-size: 14px;
font-weight: bold;
}

.entry-list .intro-article-wrapper a {
text-decoration: none;
color: #0085cd;
font-weight: bold;
font-size: 14px;
}
</style>

<div id="new-entries-title">あわせて読みたい</div>
<span id="new_entriee">記事を読込中...</span>

<script src="https://www.google.com/jsapi"></script>
<script type="text/javascript">

/* この辺を自分のブログに合わせて変更 */
var blogURL = "http://exfreeter.hatenablog.com";
var blogTITLE = "exfreeterのブログ"; 
var maxNUM = 5;
var popularNUM = 2;

/* コピーライトっぽいの */
var copyRIGHT = '<p style="text-align:right; font-size:60%;">(Powerd by '
              + '<a href="http://bulldra.hatenablog.com/entry/2013/11/09/081719"  rel="nofollow" style="font-size:110%">id:bulldra</a>)</p>';

google.load("feeds", "1");
var entries = new Array();

/* 配列にシャッフル機能を追加 */
Array.prototype.shuffle = function() {
    var i = this.length;
    while(i){
        var j = Math.floor(Math.random()*i);
        var t = this[--i];
        this[i] = this[j];
        this[j] = t;
    }
    return this;
}
  
function initialize() {  
  /* feed群の生成 */
  var feeds = new Array();
  /* ブクマ順エントリ */
  feeds.push(new google.feeds.Feed( "http://b.hatena.ne.jp/entrylist?sort=count&mode=rss&url=" +blogURL));
  /* 最新エントリ */
  feeds.push(new google.feeds.Feed( blogURL+ "/rss"));
  
  /* feed読み取り処理 */
  var c = 0;
  for(var i = 0; i < feeds.length; i++) {
    feeds[i].setNumEntries(10);
    feeds[i].load(function(result) {
        /* 読み込めないなら終了 */
        if (result.error) {
          ;
        } else if(result.feed.entries.length == 0) {
          ;
        } else {
          /* 条件に合致した場合のみ結果配列に追加 */
          entries = entries.concat(result.feed.entries);
        }
        
        /* 全部のfeedが読み終わったら処理を実施 */
        if(++c == feeds.length){
          createHtml();
        }
      });
  }  
}  
  
function createHtml() {
  /* 1件もないなら終了 */
  if (entries.length == 0) {
    return;
  }
  
  /* popularNUMで指定した分だけはてブ数上位は必ず表示 */
  var resultEntries = new Array();
  for(var x = 0; x < popularNUM; x++) {
      if(document.location.href.lastIndexOf(entries[0].link, 0) != 0){
          resultEntries.push(entries[0]);
      }
      entries.splice(0, 1);
  }
  
  /* シャッフル */
  entries = entries.shuffle();  
  
  for(var x = 0; x < entries.length && resultEntries.length < maxNUM; x++) {
      /* ブラウザで対象エントリを表示している場合はスキップ */
      if(document.location.href.lastIndexOf(entries[x].link, 0) === 0) {
        continue;
      }
      
      /* 既に結果リストに含まれていたらスキップ */
      var flg = false;
      for(var y = 0; y < resultEntries.length; y++) {
        if(resultEntries[y].link.lastIndexOf(entries[x].link, 0) === 0) {
          flg = true;
        }
      }
      if(!flg) {
          resultEntries.push(entries[x]);
      }    
  }
  
  /* 指定数のHTMLを生成 */ 
  var resultHtml = "";
  for(var x = 0; x < resultEntries.length; x++) {    
    var entry = resultEntries[x];
    var entryTitle = entry.title.replace('- '+blogTITLE , '') 
    
    /* HTML生成 */
    var html = '<div class="intro-article-wrapper" style="width: 100%; overflow:auto; margin-bottom:10px;">'
         + '<a class="intro-article-img" href="' + entry.link + '" style="float:left;" rel="nofollow">'
         + '<img src="http://capture.heartrails.com/150x130/shadow?' + entry.link 
         + '" align="left" width="150" height="130" alt="' 
         + entryTitle  + '">'
         + '<a class="intro-article-title" href="' + entry.link + '" rel="nofollow">'
         +  entryTitle 
         + '</a> <img src="http://b.hatena.ne.jp/entry/image/' + entry.link + '">'
         + '<a href="http://tweetbuzz.jp/redirect?url=' + entry.link + '"><img src="http://tools.tweetbuzz.jp/imgcount?url=' + entry.link + '"/></a></div>';
    resultHtml += html;
  }  
  
  /* コンテナに反映 */
  var container = document.getElementById("new_entriee");
  container.innerHTML = resultHtml + copyRIGHT;
}

google.setOnLoadCallback(initialize);

</script>

以上です。

(以下、12/27 07:25追記)
ちょっと説明不足でした...。
「デザインCSS」というところに以下のコードも置いてます。
上のコードのstyleタグはスマホ部分で、以下がPCですね。

/* あわせて読みたい */
#new-entries-title {
    color: #000;
    font-size: 160%;
    font-weight: bold;
    margin: 20px 0 10px 0;
    padding:0 0 0 15px;
    height: 39px;
    border-left: solid 14px #000;
    border-bottom: solid 1px #000;
}

.intro-article-wrapper a {
text-decoration: none;
color: #0085cd;
font-weight: bold;
font-size: 120%;
}