太陽がまぶしかったから

C'etait a cause du soleil.

はてなブログで表示エントリのカテゴリに関係するエントリを「あわせて読みたい」として表示するウィジェット


最新版

古いバージョンのスクリプトを利用している方はご対応をお願いします

※現在掲載しているものは修正済みです。

カテゴリに関係するエントリを「あわせて読みたい」として表示

 id:FFCCEoT_NESS さんに依頼されて、カテゴリごとのリンクを作る件について、調べていて、id:splicomさんの『【スマホ表示に対応】はてなブログで同じカテゴリーの記事へのリンクを表示する - Sprint Life』を知りました。こちらの仕組みを元にさせて頂いて以下のウィジェットを作成しました。

f:id:bulldra:20140201124005p:plain

 基本的には『はてなブログにトラックバック機能を拡張するウィジット - 太陽がまぶしかったから』と同じ手法を使っていますが、なるべくサーバーサイド側で処理を終わらせておく事で機種依存を減らしました。

スクリプト設置方法

 スマートフォン表示をする場合には当該チェックボックスをONにします。

f:id:bulldra:20140201124305p:plain

 以下のスクリプトをデザイン画面におけるエントリ下の部分に配置します。CSSクラスとブログのURL等については各自変更をお願いします。

<style>
.entry-list .hatena-module-foot {
  font-size: 12px;
}
</style>
<span id="category_relate" class="hatena-module-foot"><h3>NOW LOADING...</h3></span>

<script src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
//表示/取得数
var fetchNUM = 6;
//ヘッダとして表示する文字列(%CATEGROY% で カテゴリ名表示)
var headHtml = '<h3>カテゴリー「%CATEGROY%」の記事</h3>';
//サムネイル表示
var isCreateThumbnail = true;
//ブックマーク数表示
var isCreateBookmarkNum = true;
//ツイート数表示
var isCreateTweetNum = true;
//コピーライト
var copyRIGHT = '';

var category_element = document.querySelector('div.categories a');
if(category_element !== undefined && category_element != null) {
  var category_url = category_element.href.replace('/category','/rss/category');
  var category_title = category_element.text;
  headHtml = headHtml.replace("%CATEGROY%",category_title);
  var container = document.getElementById("category_relate");
  container.innerHTML = headHtml;

  google.load("feeds", "1");

  function category_initialize() {
    relRssUrl = 'http://pipes.yahoo.com/pipes/pipe.run?URL=' 
              +  category_url
              +  '&MYURL='
			  +  document.location.href
              + '&_id=dd5bff7c8a25829661a27295ed7ed2d4&_render=rss'
              + '&num=' + fetchNUM;
    console.log(relRssUrl)
    var feed = new google.feeds.Feed(relRssUrl);
    feed.setNumEntries(fetchNUM);
    feed.load(function(result) {
      if (result.error || result.feed.entries.length == 0) {
	    return;
      } else {
	    console.log(result.feed.entries.length)
	    category_createHtml(result.feed.entries);
      }
    });
  }

  function category_createHtml(resultEntries) {
    /* 1件もないなら終了 */
    if (resultEntries.length == 0) {
      return;
    }
  
    /* 指定数のHTMLを生成 */ 
    var resultHtml = "";
    for(var x = 0; x < resultEntries.length; x++) {	
      var entry = resultEntries[x]; 
		 
	  var html =createThumbnail(isCreateThumbnail, entry.link)
	     + createLink(entry.title, entry.link)

      if(!isCreateThumbnail) { 
        html = '<li>' 
             + html + " "
             + createBookmarkNum(isCreateBookmarkNum, entry.link)
             + createTweetNum(isCreateTweetNum, entry.link)
             + '</li>';
	  } else {
        html = '<div class="intro-article-wrapper" style="width: 100%; font-size:110%; '
             + 'overflow:auto; margin-bottom:10px;>'
			 + html + "<br />"
			 + createBookmarkNum(isCreateBookmarkNum, entry.link)
             + createTweetNum(isCreateTweetNum, entry.link)
		     + '</div>';
	  }
      resultHtml += html;
    }
	
    if(!isCreateThumbnail) {
	  resultHtml = '<div style="margin-left:30px; font-size:90%">'
	    + '<ul style="list-style-type:circle">' + resultHtml + '</ul></div>';
    }
	
    var container = document.getElementById("category_relate");
    container.innerHTML = headHtml + resultHtml + copyRIGHT;
  }
  
  google.setOnLoadCallback(category_initialize);
}  else {
  var container = document.getElementById("category_relate");
  container.innerHTML = "";
}
  
function createThumbnail(isExe, entryLink) {
  if(!isExe) {
    return "";
  } else {
      var result = '<a class="intro-article-img" href="'
        + entryLink
        + '" style="float:left;" rel="nofollow">'
        + '<img src="http://capture.heartrails.com/150x130/shadow?' 
        + entryLink 
        + '" align="left" width="150" height="130" alt="' 
        + entryLink  + '">'
      return result;
  }
}
  
function createLink(entryTitle, entryLink) {
  var result = '<a class="intro-article-title" href="' 
       + entryLink
       + '" rel="nofollow">'
       + entryTitle
       + '</a>'
  return result;
}
  
function createBookmarkNum(isExe, entryLink) {
  if(!isExe) {
	return "";
  }
  var result = '<a href="http://b.hatena.ne.jp/entry/'
     + entryLink
     + '" rel="nofollow" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/'
     + entryLink
     + '"style="opacity:0.8;" /></a>'
  return result;
}
  
function createTweetNum(isExe, entryLink) {
  if(!isExe) {
	return "";
  }
  var result = ' <a href="http://tweetbuzz.jp/redirect?url='
     + entryLink
     + '" rel="nofollow"  target="_blank">'
     + '<img src="http://tools.tweetbuzz.jp/imgcount?url='
     + entryLink
     + '"/></a>';
  return result;
}
</script>


「トラックバック機能」と同時に使う

 トラックバックと併用すると記事が重複してしまうのだけれどなんとかなりませんか?(人任せ」というブコメを頂きました。、同時に使用する場合はこちらをご利用いただければと思います。

<style>
.entry-list .hatena-module-foot {
  font-size: 12px;
}
</style>
<span id="trackback_entry" class="hatena-module-foot"></span>
<span id="category_relate" class="hatena-module-foot"><h3>NOW LOADING...</h3></span>

<script src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
//表示/取得数
var fetchNUM = 6;
var blogURL = "";
//トラックバックのヘッダとして表示する文字列
var headerTITLE_TRACKBACK = '<h3>トラックバック一覧</h3>'
//カテゴリヘッダとして表示する文字列(%CATEGROY% で カテゴリ名表示)
var headerTITLE_CATEGORY = '<h3>カテゴリー「%CATEGROY%」の記事</h3>';
//サムネイル表示
var isCreateThumbnail = true;
//ブックマーク数表示
var isCreateBookmarkNum = true;
//ツイート数表示
var isCreateTweetNum = false;

//コピーライト
var copyRIGHT = '';

var category_element = document.querySelector('div.categories a');
if(category_element !== undefined && category_element != null) {
  var category_url = category_element.href.replace('/category','/rss/category');
  var category_title = category_element.text;
  headerTITLE_CATEGORY = headerTITLE_CATEGORY.replace("%CATEGROY%",'<a href="' + category_element.href  + '">' + category_title + '</a>');
  var container = document.getElementById("category_relate");
  container.innerHTML = headerTITLE_CATEGORY;

  google.load("feeds", "1");

  function category_initialize() {
    relRssUrl = 'http://pipes.yahoo.com/pipes/pipe.run?URL=' 
              +  category_url
              +  '&MYURL='
    		  +  document.location.href
              + '&_id=dd5bff7c8a25829661a27295ed7ed2d4&_render=rss'
              + '&num=' + fetchNUM;
    console.log(relRssUrl)
    var feed = new google.feeds.Feed(relRssUrl);
    feed.setNumEntries(fetchNUM);
    feed.load(function(result) {
      if (result.error || result.feed.entries.length == 0) {
	    return;
      } else {
	    createHtml('category_relate',headerTITLE_CATEGORY,result.feed.entries);
      }
    });
  }
  
  google.setOnLoadCallback(category_initialize);
}  else {
  var container = document.getElementById("category_relate");
  container.innerHTML = "";
}


/* スクリプトを生成してheadに追加 */
var userScript = document.createElement('script');
userScript.type="text/javascript";
userScript.src='http://b.hatena.ne.jp/entry/jsonlite/?url=' 
          + encodeURIComponent(location.href) 
          + '&callback=callbackBookmark';
document.getElementsByTagName('head')[0].appendChild(userScript);

/* コールバック関数 */
var relRssUrlTrackBack = "";
function callbackBookmark(bookmark) {
  if(bookmark) {
    /* eid取得 */
    var eid = bookmark.eid;
    if(eid == '') {
      return;
    }
    /* このエントリーを含むエントリー取得URL生成 */
    var relUrl = 'http://b.hatena.ne.jp/fragments/entry.reldiary?'
               + 'key=fragments%2Fentry.reldiary.html%3Aeid%3D'
               + eid + '&ttl=1800&keys=eid%3D' + eid;
    console.log(relUrl);
    relRssUrlTrackBack = 'http://pipes.yahoo.com/pipes/pipe.run?URL=' 
              + encodeURIComponent(relUrl) 
              + '&_id=da86ac3f59f0ff92c537c0d8d5952b94&_render=rss'
          + '&blog=' + encodeURIComponent(blogURL);
              + '&num=' + fetchNUM
  } 
}

google.load("feeds", "1");
var entriesTrackBack = new Array();
function initializeTrackBack() {  
  /* feed群の生成 */
  var feed = new google.feeds.Feed(relRssUrlTrackBack);
  console.log(relRssUrlTrackBack);

  /* feed読み取り処理 */
  feed.setNumEntries(fetchNUM);
  feed.load(function(result) {
    if (result.error || result.feed.entries.length == 0) {
      return;
    } else {
      createHtml('trackback_entry', headerTITLE_TRACKBACK,result.feed.entries)
    }
  });  
}  
google.setOnLoadCallback(initializeTrackBack);

function createHtml(spanid, headerHtml, resultEntries) {
  /* 1件もないなら終了 */
  if (resultEntries.length == 0) {
    return;
  }
  
  /* 指定数のHTMLを生成 */ 
  var resultHtml = "";
  for(var x = 0; x < resultEntries.length; x++) {    
    var entry = resultEntries[x]; 
    	 
	var html =createThumbnail(isCreateThumbnail, entry.link)
	     + createLink(entry.title, entry.link)

    if(!isCreateThumbnail) { 
      html = '<li>' 
         + html + " "
         + createBookmarkNum(isCreateBookmarkNum, entry.link)
         + createTweetNum(isCreateTweetNum, entry.link)
         + '</li>';
	} else {
      html = '<div class="intro-article-wrapper" style="width: 100%; font-size:110%; '
             + 'overflow:auto; margin-bottom:10px;>'
			 + html + "<br />"
			 + createBookmarkNum(isCreateBookmarkNum, entry.link)
             + createTweetNum(isCreateTweetNum, entry.link)
		     + '</div>';
	}
    resultHtml += html;
  }
	
  if(!isCreateThumbnail) {
	resultHtml = '<div style="margin-left:30px; font-size:90%">'
	    + '<ul style="list-style-type:circle">' + resultHtml + '</ul></div>';
  }
  var container = document.getElementById(spanid);
  container.innerHTML = headerHtml + resultHtml + copyRIGHT;
}
  
function createThumbnail(isExe, entryLink) {
  if(!isExe) {
    return "";
  } else {
      var result = '<a class="intro-article-img" href="'
        + entryLink
        + '" style="float:left;" rel="nofollow">'
        + '<img src="http://capture.heartrails.com/150x130/shadow?' 
        + entryLink 
        + '" align="left" width="150" height="130" alt="' 
        + entryLink  + '">'
      return result;
  }
}
  
function createLink(entryTitle, entryLink) {
  var result = '<a class="intro-article-title" href="' 
       + entryLink
       + '" rel="nofollow">'
       + entryTitle
       + '</a>'
  return result;
}
  
function createBookmarkNum(isExe, entryLink) {
  if(!isExe) {
	return "";
  }
  var result = '<a href="http://b.hatena.ne.jp/entry/'
     + entryLink
     + '" rel="nofollow" target="_blank"><img src="http://b.hatena.ne.jp/entry/image/'
     + entryLink
     + '"style="opacity:0.8;" /></a>'
  return result;
}
  
function createTweetNum(isExe, entryLink) {
  if(!isExe) {
	return "";
  }
  var result = ' <a href="http://tweetbuzz.jp/redirect?url='
     + entryLink
     + '" rel="nofollow"  target="_blank">'
     + '<img src="http://tools.tweetbuzz.jp/imgcount?url='
     + entryLink
     + '"/></a>';
  return result;
}
</script>

その他の修正(本ブログ用カスタマイズ)

  • ツイート数表示については、『http://tweetbuzz.jp/static/imgcounter』を利用しているのですが、どうも動作が不安定なので、デフォルトでオフにします。利用する場合には「isCreateTweetNum」をtrueにしてください。
  • 見出しのカテゴリ名にリンクから、カテゴリ毎ページにリンクを張るようにしました。
  • トラックバックについて一度に大量に取得してランダム順に表示する処理は削除しています

今後のサポートと謝辞

 はてなブログ側の仕様変更に対応できない事もあると思いますし、バグがあったりする可能性もあるので自己責任でご利用下さい。変更も各自でご自由に。なお、設置サポートや表示スタイル/アルゴリズムのカスタマイズ等のご依頼がありましたらサポートライセンスの購入をお願いいたします。

 実装するのにあたり、id:splicomさんの『【スマホ表示に対応】はてなブログで同じカテゴリーの記事へのリンクを表示する - Sprint Life』のコードを一部利用させて頂きました。ありがとうございます。

よくわかるJavaScriptの教科書

よくわかるJavaScriptの教科書