PC版の相互RSSを見ていて思いました。
リンクテキストの横に、そのサイトのファビコンが表示されてるとカッコいいですよね。
でも… 弊ブログのファビコンはこの↑一萬の麻雀牌なんですが、相互RSSだと めくれた紙のアイコンが表示されてしまいます。

今回は、これを何とかしたいというお話です。

ライブドア相互RSSのファビコン問題

相互RSSでは、ちゃんとそのページのファビコンが取得できているものもあれば、ウチのようにできていないものもあります。
ブラウザの開発者ツールでファビコンのタグを見ると、その参照元は
<img class="blogroll-favicon" src="http://blogroll.livedoor.net/url/http://humotomiti.blog.jp/" width="16" height="16">
blogroll.livedoor.net/url/というパスにある GIF画像のようです。
つまり、サイトの指定しているファビコンを直接参照しているのではなく、ライブドアの専用のドメインに保存した画像を読みに行っているわけです。
その取得がうまくいっていないと、めくれた紙のデフォルト画像が表示されるようですね。

一般的に、ファビコンはサイトのドメイン直下にfavicon.icoという名前で保存することが慣習になっているので、http://ドメイン/favicon.icoというURL指定で参照できることが多いわけですが、正規のやり方としては、ページの<head>タグの中に<link>で指定し、ブラウザもこれを読んでファビコンを表示します。
その場合はfavicon.icoという名前でなくてもいいのですが、そうすると今度は、いったんページを読み込まないとファビコンのURLがわかりません。
そこで…

他社のAPIを使う

Google または はてなの API を利用します。
ともに有名サイトなので基本的には安心して使えますが、サーバーの忙しい時間帯には動作しないことがあるようです。
また、非公開APIということなので、ある日突然仕様が変更されたり、使えなくなるリスクもあります。
それを踏まえた上でも、この両者が最も信頼性の高いAPIを提供していると言えると思います。

なお API というのは、ざっくりと言うと「なんか面倒な作業を自動でやってくれるプログラム全般」のことで、ここでは「WEBページのアドレスを渡すとそのページのファビコンを画像にして返してくれる仕組み」ということになります。
  • Google API

    https://www.google.com/s2/favicons?domain=ドメインを指定。
    https://www.google.com/s2/favicons?domain=humotomiti.blog.jp/
    ここで言うドメインは、ページのURLから先頭のhttp(s)://を除いたもの。
    解説サイト様はどこもドメインで指定するように、と書いていましたが、現在はhttp(s)://を除いても除かなくても動作しています。
    imgタグのsrc属性や、HTMLのインライン/ブロック要素のbackground:urlプロパティに、このURLを指定します。 うーん、なぜか Googleさん自身のファビコンが取得できてませんね。
    あと、弊ブログのファビコンは久しくfavicon.icoというICO画像ファイルだったのですが、これがうまく取得できないようだったので、 PNG画像に差し替えたらうまく行きました。
  • はてな API

    http://www.google.com/s2/favicons?domain=ページのURLを指定。
    http://favicon.hatena.ne.jp/?url=http://humotomiti.blog.jp/
    こちらでは google.co.jp がちゃんと取得できていますが、まとめアンテナ様のファビコンが見慣れないデザインになっています。もしかして、初期の画像データが残っているのでしょうか?
    弊ブログのは取得できてません…。
    ブクマされたことのないページは、ファビコンのデータが作られておらず、表示できないそうです。
    数少ないブクマされたページでは、ファビコンが取得できています。 が、両サイドの透明部分がトリミングされて、横に伸ばされていますね(笑)
    トップページは…自分でブクマするのも恥ずかしいので、頑張るしかありません。

jQuery を使う

さて、以上の方法だと、リンク先のURLとAPIに渡すURLがせっかく共通なのに同じアドレスを二回書かなければならず、コードが冗長になるというデメリットがあります。
手作業だとコピペ漏れなどのミスも起きやすいですし。
そこで、HTMLソースには<a>タグだけを書き、ファビコンの表示処理は jQuery で行うという方法があります。
なお、下記の JavaScriptコードはいずれも
$(function(){
	/* 処理 */
})
の、/* 処理 */の部分です。
  • ファビコンをbackground属性に指定する

    見出しのまんまですが、ファビコンを<a>タグの背景画像として指定する方法です。
    例えばこんなリンクリストがあったとすれば
    <ul class="link-list">
        <li><a href="http://aaa.com">サイトA</a></li>
        <li><a href="http://bbbbbbbb.com">サイトB</a></li>
        <li><a href="http://cccccccc.com">サイトC</a></li>
        <li><a href="http://dddddddd.com">サイトD</a></li>
    </ul>
    ここの<a>タグのセレクタはul.link-list>li>aなので、
    $("ul.link-list>li>a").each(function(){
        $(this).css({
            background: "url(http://favicon.hatena.ne.jp/?url="
                      + this.href
                      + ") left center no-repeat",
            backgroundSize: "16px 16px",
            padding: "0 0 0 20px"
        })
    })
    とすれば、リンクテキストの左側に、はてなAPIを介してファビコンが表示されます。
    動作が安定しているなら、css()メソッドで指定しているプロパティのうち、最後の二つはあらかじめスタイルシートで指定しておいてもいいでしょう。
    ul.link-list>li>a{
        background-size: 16px 16px;
        padding: 0 0 0 20px;
    }
  • img要素を生成する

    こちら↓からコピペしています(セレクタは上の例に合せました)。
    $('ul.link-list>li>a').filter(function(){
    	return this.hostname && this.hostname !== location.hostname;
    }).each(function(){
    	var l = $(this);
    	var fu = l.attr('href').replace(/^(http:\/\/[^\/]+).*$/, '$1') + '/favicon.ico';
    	var fi = $('<img src="favicon.png" alt="favicon" />')['prependTo'](l);
    	var e = new Image();
    	e.src = fu;
    	if (e.complete) fi.attr('src', fu);
    	else e.onload = function(){
    		fi.attr('src', fu);
    	};
    });
    <a>タグを見つけて、そのURLのドメインにある(はずの)favicon.icosrc属性に指定したimg要素を挿入する、というスクリプトですね。
    favicon.icoが無い場合のデフォルトアイコンにはfavicon.pngが指定されています。
    変数fuの代入演算を見ると、既存のAPIを使っていない、言わばこのスクリプト自体がAPIであると言えます。
    ただ、これはドメインのルートにfavicon.icoを置く、という慣例頼りなので、うまく表示されない場合も多そう。fuには上記APIを指定しでもいいかも知れません。
    私ならhtml()メソッドで HTMLタグをダーッと書き込んで済ますでしょうけど、こちらは正攻法で DOMの構造を操作していて、とてもスマート。
    あと、今回のテーマには関係ありませんが6行目でメソッド.prependTo()を連想配列のプロパティ名のように書いているのがカッコいいと思いました。
    こんな書き方ができるんですね。ちょっと真似できません。
    これが…(スキルの)国境…

相互RSSのファビコンを Google API 仕様に

というわけで、弊ブログでは以下の方法でファビコンを表示するようにしました。
  1. 相互RSSの設定でファビコン表示をオフにし、リンクテキストだけが表示されるようにします。
  2. その上で、以下のスクリプトタグをフリーエリアなどに貼って実行し、Google API で取得されるファビコンを、リンクテキストの背景画像に指定します。
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script>
    $(function(){
    	$("a.blogroll-link").each(function() {
    		var domain = $(this).attr('href').replace(/http:\/\//, '');
    		$(this).css({
    			background: "url(http://www.google.com/s2/favicons?domain=" + domain + ") left center no-repeat",
    			backgroundSize: "16px 16px",
    			paddingLeft: "20px"
    		});
    	});
    });
    </script>
    ※jQuery が導入済なら一行目は不要
結果、相互RSSのデフォルトよりは精度の高いファビコン表示ができました。
ただ、相互RSSのスクリプト展開とは非同期で実行されるため、展開が遅くなった場合は表示されないことがあります。