皆大好き、はてなブックマークウィジェットについてのトピックです。
このウィジェットがちょっと問題があるので、色々解決策を考えてみたいと思います。
問題点
httpsの画面で使えない
はてなブックマークウィジェットで読み込まれるjsですが・・・
hatenadiary: { wrapper:'hatena-module', title: 'hatena-moduletitle', body: 'hatena-modulebody', footer: 'hatena-modulefooter', cssfile:'http://b.hatena.ne.jp/css/widget-hatenadiary.css' },
(;^ω^) うわぁ・・http固定でcssを読み込んでる・・
皆さんはhttpsの画面はhttpのリソースを読み込むと警告が表示される仕様はご存知でしょうか。js、css、画像ファイルが主な警告の原因となりがちです。
google adsenseで表示される画像がhttpであっても警告が表示されてしまいます。IEの場合は画面下部にデカデカと警告バーが表示される程の鬱陶しさです。
こんな警告が出てしまってはhttpsの画面ではてなブックマークウィジェットは使えません・・・
キャッシュが効かない
js自体のキャッシュは効きます、が、jsで取得するブックマークの内容まではキャッシュできません。
内部的にはブックマーク情報をjsonで取得しています。つまり画面を表示する度にjsonを取得、という無駄な処理をしています。
物凄い勢いでブックマークされるサイトならともかく、当ブログのようにPV数が少ないサイトでは、キャッシュをして4時間に1回程度の頻度でキャッシュ破棄・更新、をすれば十分です。
非同期スクリプトではない
widget.jsはピュアjsで記述されていますが、はてなのサーバにjsonをリクエストする部分等、非同期処理は全く記述されていません。つまりウィジェットの処理中はhtmlの描画処理はブロック(停止)されます。これは大問題です。
対応策
自前でウィジェット作っちゃえ!!
まずはウィジェットのコードを見てみます。widget.jsを読み込んでますね。
<div class="hatena-widget widget-hatena-bookmark-widget"> <script language="javascript" type="text/javascript" src="http//b.hatena.ne.jp/js/widget.js" charset="utf-8"></script> <script language="javascript" type="text/javascript"> Hatena.BookmarkWidget.url = "http://treeapps.hatenablog.com/"; Hatena.BookmarkWidget.title = "treeのメモ帳の人気記事"; Hatena.BookmarkWidget.sort = "count"; Hatena.BookmarkWidget.width = 145; Hatena.BookmarkWidget.num = 10; Hatena.BookmarkWidget.theme = "default"; Hatena.BookmarkWidget.load(); </script>
ではwidget.jsの中身を解読してみます。
var createRequest = function(id) { var url = Hatena.BookmarkWidget.url; var sort = Hatena.BookmarkWidget.sort; return function () { var request = 'http://b.hatena.ne.jp/entrylist/json?' + [ 'callback=Hatena.BookmarkWidget.callbacks['+ id +']', 'url=' + encodeURIComponent(url), 'sort=' + sort ].join('&'); var script = $E('script', { defer: 'defer', type: 'text/javascript', charset: 'utf-8', src: request }); document.getElementsByTagName('head')[0].appendChild(script) } };
( ^ω^) json?これだ!!
ここではてなのサーバに対してブックマーク情報をjsonで取得するリクエストを生成しているようです。このコードを元に、jsonを取得するurlを作ってみます。すると以下のようになります。
http://b.hatena.ne.jp/entrylist/json?&url=http://treeapps.hatenablog.com/&sort=count
これを実行するとjsonが返ります。ということは・・・?
( ^ω^) 自前でウィジェットを作れる!
jsonをパースしてEHCache等でキャッシュし、自分でウィジェットのhtmlを書けばよいのです。EHCacheに関しては以前に記事を書いたので、合わせてご覧下さい。
更に、javaでhttpリクエストをし、json or xmlをDTOにバインドする簡単ライブラリの記事も書いたので、是非ご覧下さい!
後日談:実際やってみた!
今回はjavaで実装します。JSONをDTOにマッピングするために、JSONICを使うのでクラスパスに通します。
JSONIC - simple json encoder/decoder for java
guavaも使うので、合わせてクラスパスに通します。
GitHub - google/guava: Google core libraries for Java
DTO
public class HatenaBookmarkWidgetDto { private List<Bookmark> bookmarks; public static class Bookmark { private String link; private String count; private String title; public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getCount() { return count; } public void setCount(String count) { this.count = count; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } public List<Bookmark> getBookmarks() { return bookmarks; } public void setBookmarks(List<Bookmark> bookmarks) { this.bookmarks = bookmarks; } }
jsonを取得してDTOにマッピングする
public List<Bookmark> getHatenaBookmarkWidget() { URL url = new URL("http://b.hatena.ne.jp/entrylist/json?&url=http://treeapps.hatenablog.com/&sort=count"); BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(url.openStream(), "UTF8")); List<String> lines = Lists.newArrayList(); String line; while ((line = br.readLine()) != null) lines.add(line); String json = Joiner.on(' ').join(lines); // jsonpをjsonに変換 StringBuilder sb = new StringBuilder(); sb.append("{ bookmarks : ["); sb.append(json.substring(2, json.length() - 3)); sb.append("]}"); HatenaBookmarkWidgetDto hatenaBookmark = JSON.decode(sb.toString(), HatenaBookmarkWidgetDto.class); return hatenaBookmark.getBookmarks(); } catch (Exception e) { e.printStackTrace(); } finally { Closeables.closeQuietly(br); } }
取得したjsonは実はjsonpなので、jsonに無理やり変換してからDTOにバインドしてます。後はこれを画面に表示するだけです。↓tree-tipsではこんな感じで実装してみました。
自由にデザインできるんですが、ユーザの慣れの観点を考慮して敢えて見た目を似せました。
実物を見たい方はこちらです↓↓↓