rcmdnk's blog
Last update

20141215_socialbuttons_200_200

ソーシャルボタンをそれぞれのサービスのオフィシャルな物を使ってましたが、 それぞれ見た目が余り揃ってなくて良くないのと、 幅の取り方とかがCSSだけで行うのも難しくて微妙にずれてたりしました。 それと読み込みにもそれなりに時間がかかります。

ということで、画像とかを呼んでこなくて済むように適当に 自分で作ってみましたというよくある話。

作り方

それぞれのサービスのAPIを使ってカウント数を取ってきます。 Twitterなら

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var url = encodeURI(location.href);
socialData = {
  type: 'GET',
  dataType: 'jsonp',
  url: 'http://urls.api.twitter.com/1/urls/count.json',
  data: {
    url: url,
    noncache: new Date().getTime()
  },
  success: function(data){
    $('.twitterCount').text(data.count||0);
  }
};
$.ajax(socialData);

みたいにして、HTMLで

1
2
3
    <li><a href="http://twitter.com/intent/tweet?text={title}&url={url}&via={author}"
    title="Share on Twitter"
    target="_blank"><p class="twitter_custom"><i class="fa fa-twitter"></i> <span class="twitterCount"></span></p></a></li>

みたいに書いておけばspanの要素の部分にカウント数を入れてくれます。

追記: 2015/12/17

ツイート数の取得は現在上のAPIが廃止されこの方法では取れません。 差し当たり下に書いたcount.jsoon等を使うと取得することが出来ます。

追記ここまで

後はこのHTMLの例みたいにリンク先を現ページのTweetのものにして Font Awesomeとかで飾ってあげればOK。

後はこのリンクの背景とかを適当に飾ってあげます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$twitter-color: #00ACEE;
p {
  border: solid 1px;
  border-radius: 3px;
  float: left;
  margin-right: 2px;
  padding: 0px 10px 3px;
  font-size: 13px;
  height: 18px;
  color: #FFFFFF;
  text-decoration: none;
  &:hover {color: darken(#FFFFFF, 10);}
  &.twitter_custom {
    background-color: $twitter-color;
    border-color: $twitter-color;
    &:hover {
      background-color: darken($twitter-color, 10);
      border-color: darken($twitter-color, 10);
    }
  }
}

で、こんな感じになります。

twitter

APIを使って取ってくるときに、 Google+とPocketに関しては直接数を取ってくるようなAPIが公開されていないので、 通常のソーシャルボタンを取ってきてそこから数を抜き出します。

この時、クライアント側でJavaScriptだけでやろうとすると、 クロスドメイン制約がかかって素直に取れないので、 YahooのYQLというサービスを中継して抜き出す必要があります 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var url = encodeURI(location.href);
socialData = {
  type: 'GET',
  dataType: 'jsonp',
  url: "http://query.yahooapis.com/v1/public/yql",
  data: {
    q: "SELECT content FROM data.headers WHERE url='https://plusone.google.com/_/+1/fastbutton?hl=ja&url=" + url + "'",
    format: "jason",
    env: "http://datatables.org/alltables.env";
    noncache: new Date().getTime()
  },
  success: function(data){
    var m = data.query.results.resources.content.match(/window\.__SSR = {c: ([\d]+)/);
    $('.googleplusCount').text((m != null)? m[1] : 0);
  }
};
$.ajax(socialData);

こんな感じ。ボタンを取得しに行くので残念ながらこの場合は余計に時間がかかってしまいます。

もしくはサードパーティーで集計してくれるAPIを公開してる物もあるので そちらを使う手もあります。 いくつかのものをまとめて返してくれる様な物も結構あるので2 そういうのを使って一気に取るのもありかも。 (サードパーティーのだと途中で終了してしまう可能性がありますが、 公式でも変更される事はあるし、沢山同じようなサービスがあるので別に使っても良いかな、と。 後は速度との兼ね合い。)。

そんな感じで作ってみたテストページが下のもの。 こちらは、はてなブックマーク、Twitter、Facebook、Pocket、LinkedIn が入ったもので、自作板と公式版それぞれのページがあり、 それぞれhttps://www.google.com(またはhttps://www.linkedin.com))とhttp://rcmdnk.github.io/aaaaa を指しています。 (それなりにカウント数があるものと0のものの例として。)

両方共全て1つのHTML内にcssやJavaScriptなども書いてあるので、 やってることはソースを見てください。。。

比べてみると、特に公式版のFacebookの表示に時間がかかってますが、 自作版の方が圧倒的にスムーズに表示されてると思います。

また、上に加えて、StumbleUpon、Pinterest、Delicious、Tumblr 等を加えたのが下のもの。

こちらはStumbleUponとDeliciousのカウント数の取得が自作板で上手く行ってません。 StumbleUponの方は直接ブラウザで見たりすると数がきちんと見れるんですが 上の方法でやろうとするとエラーが出てて、 Deliciousの方はブラウザで見ても、たまに成功したりしなかったりします (一定時間に何回、とか制限がかかってる感じ?)。

また、Tumblrは数を取ってません。(分からない。) 後、Pinterestの公式版の方で数付きの表示の仕方が分からなかったので数抜きで。

Octopress用に追加

まず、JavaScript部をsource/javascripts/shareCustom.jsなどという名前の スクリプトを作って、

source/javascripts/shareCustom.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
var socialCount = function (social, cname, url) {
  var vname = cname.replace(/-/g, "");
  if((new Function('return (typeof ' + vname + '!= "undefined");'))())return;
  (new Function('window.' + vname + ' = "defined";'))();
  url = (url)? encodeURI(url): encodeURI(location.href);
  socialData = {
    type: 'GET',
    dataType: 'jsonp',
    data: {noncache: new Date().getTime()}
  };
  if(social == 'hatebu'){
    socialData.url = 'http://api.b.st-hatena.com/entry.count';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text(data||0);
    };
  }else if (social == 'twitter'){
    socialData.url = 'http://urls.api.twitter.com/1/urls/count.json';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text(data.count||0);
    };
  }else if(social == 'googleplus'){
    socialData.url = "http://query.yahooapis.com/v1/public/yql";
    socialData.data.q = "SELECT content FROM data.headers WHERE url='https://plusone.google.com/_/+1/fastbutton?hl=ja&url=" + url + "'";
    socialData.data.format = "json";
    socialData.data.env = "http://datatables.org/alltables.env";
    socialData.success = function (data) {
      var m = data.query.results.resources.content.match(/window\.__SSR = {c: ([\d]+)/);
      $('.' + cname).text((m != null)? m[1] : 0);
    };
  }else if(social == 'facebook'){
    socialData.url = 'http://graph.facebook.com/';
    socialData.data.id = url;
    socialData.success = function(data){
      $('.' + cname).text(data.shares||0);
    };
  }else if(social == 'pocket'){
    socialData.url = "http://query.yahooapis.com/v1/public/yql";
    socialData.data.q = "SELECT content FROM data.headers WHERE url='https://widgets.getpocket.com/v1/button?label=pocket&count=vertical&v=1&url=" + url + "'";
    socialData.data.format = "json";
    socialData.data.env = "http://datatables.org/alltables.env";
    socialData.success = function (data) {
      //$('.' + cname).text(data.toSource());
      $('.' + cname).text(data.query.results.resources.content.match(/<em</ id/="cnt">(\d+)<\</em></)[/1]||0);
      $('.' + cname).text(data.query.results.resources.content.match(/<em id="cnt">(\d+)<\/em>/)[1]||0);
    };
  }else if(social == 'linkedin'){
    socialData.url = 'http://www.linkedin.com/countserv/count/share';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text(data.count||0);
    };
  }else if(social == 'stumble'){
    socialData.url = 'http://www.stumbleupon.com/services/1.01/badge.getinfo';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text(data.result.views||0);
    };
    socialData.error = function(data){
      $('.' + cname).text(0);
    };
  }else if(social == 'delicious'){
    socialData.url = 'http://feeds.del.icio.us/v2/json/urlinfo/data';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text((data.length>0)? data[0].total_posts : 0);
    };
  }else if(social == 'pinterest'){
    socialData.url = 'http://api.pinterest.com/v1/urls/count.json';
    socialData.data.url = url;
    socialData.success = function(data){
      $('.' + cname).text(data.count||0);
    };
  }else {
    return;
  }
  $.ajax(socialData);
}

こんな感じで。

最初の所で、複数回呼ばれても同じ物に対しては1回しか実行しないように グローバルな変数をクラス名で作って処理しています3

次に、これをheadの中で

source/_includes/head.html
1
2
3
4
5
...
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
...
{% if site.share_custom %}<script src="{{root_url}}/javascripts/shareCustom.js"></script>{% endif %}
...

こんな感じで呼びます。

ここで、自作版を呼ぶときには_config_ymlの中で、

# Share
share_official: false
share_custom: true

といった感じに設定しておくことにします。

次に、実際に表示する場所として、Octopressにはsource/_includes/post/sharing.html というファイルがありますが、 同じ所にsharing_custom.htmlというファイルを以下のように作ります。

source/_includes/post/sharing_custom.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
{% if index %}{% capture share_url %}{{ site.url }}{{ post.url }}{% endcapture %}{% capture slug_url %}{{ post.url | slugify}}{% endcapture %}{% capture share_title %}{{ post.title }}{% endcapture %}{% else %}{% capture share_url %}{{ site.url }}{{ page.url }}{% endcapture %}{% capture slug_url %}{{ page.url | slugify}}{% endcapture %}{% capture share_title %}{{ page.title }}{% endcapture %}{% endif %}
<div class="share-button">
  <ul>
    {% if site.hatena_button %}
    <li><a href="http://b.hatena.ne.jp/entry/{{share_url}}"
    title="Save to Hatebu"
    target="_blank"><p class="hatebu_custom" >B! <span class="hatebuCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.twitter_tweet_button %}
    <li><a href="http://twitter.com/intent/tweet?text={{share_title}}&url={{share_url}}&via={{site.author}}"
    title="Share on Twitter"
    target="_blank"><p class="twitter_custom"><i class="fa fa-twitter"></i> <span class="twitterCount{{slug_url}}"></span></p></a></li>
    </li>
    {% endif %}
    {% if site.google_plus_one %}
    <li><a href="https://plus.google.com/share?url={{share_url}}&title={{share_title}}"
    title="Share on Google+"
    target="_blank"><p  class="googleplus_custom"><i class="fa fa-google-plus"></i> <span class="googleplusCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.facebook_share %}
    <li><a href="http://www.facebook.com/sharer.php?src=bm&u={{share_url}}&amp;t={{share_title}}"
    title="Share on Facebook+"
    target="_blank"><p  class="facebook_custom"><i class="fa fa-facebook"></i> <span class="facebookCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.pocket_button %}
    <li><a href="http://getpocket.com/edit?url={{share_url}}&title={{share_title}}"
    title="Save to Pocket"
    target="_blank"><p  class="pocket_custom">P <span class="pocketCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.linkedin_button %}
    <li><a href="http://www.linkedin.com/shareArticle?mini=true&url={{share_url}}&title={{share_title}}&source={{site.author}}"
    title="Share on LinkedIn"
    target="_blank"><p  class="linkedin_custom"><i class="fa fa-linkedin"></i> <span class="linkedinCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.stumble_button %}
    <li><a href="http://www.stumbleupon.com/badge/?url={{share_url}}"
    title="Save to StumbleUpon"
    target="_blank"><p  class="stumble_custom"><i class="fa fa-stumbleupon"></i> <span class="stumbleCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.pinterest_button %}
    <li><a href="http://pinterest.com/pin/create/button/?url={{share_url}}"
    title="Save to Pinterest"
    target="_blank"><p  class="pinterest_custom"><i class="fa fa-pinterest"></i> <span class="pinterestCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.delicious_button %}
    <li><a href="https://del.icio.us/save?v=5&provider={{site.author}}&noui&jump=close&url={{share_url}}&title={{share_title}}"
    title="Save to Delicouns"
    target="_blank"><p  class="delicious_custom"><i class="fa fa-delicious"></i> <span class="deliciousCount{{slug_url}}"></span></p></a></li>
    {% endif %}
    {% if site.tumblr_button %}
    {% if index %}{% capture description %}{% if post.description %}{{ post.description }}{% endif %}{% endcapture %}{% else %}{% capture description %}{% if page.description %}{{ page.description }}{% else %}{{ content | excerpt }}{% endif %}{% endcapture %}{% endif %}
    {% capture description %}{{ description | strip_html | codense_spaces | truncate:150 }}{% endcapture %}
    <li><a
    href="https://del.icio.us/save?v=5&provider={{site.author}}&noui&jump=close&url={{share_url}}&title={{share_title}}"
    title="Share on Tumblr"
    target="_blank"><p  class="tumblr_custom"><i class="fa fa-tumblr"></i></p></a></li>
    {% endif %}
  </ul>
</div>
<script type="text/javascript">
{% if site.hatena_button %}socialCount('hatebu', 'hatebuCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.twitter_tweet_button %}socialCount('twitter', 'twitterCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.google_plus_one %}socialCount('googleplus', 'googleplusCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.facebook_share %}socialCount('facebook', 'facebookCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.pocket_button %}socialCount('pocket', 'pocketCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.linkedin_button %}socialCount('linkedin', 'linkedinCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.stumble_button %}socialCount('stumble', 'stumbleCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.pinterest_button %}socialCount('pinterest', 'pinterestCount{{slug_url}}', '{{share_url}}');{% endif %}
{% if site.delicious_button %}socialCount('delicious', 'deliciousCount{{slug_url}}', '{{share_url}}');{% endif %}
</script>

各ボタンのオンオフは前に書いたようにhatena_buttonなどの フラッグを_config.ymlの中で指定して行います。

シェアボタンを非同期読み込みでまとめて設定

また、URLを使ってユニークなクラス名を付けたいので上のようなことを行ってるのですが、 クラス名には/等の記号が使えないので、 ここではクラス名をページのURLからJekyllのslugifyというフィルタを使って、 アルファベット以外を-にするという変換を行っています。

これを更にJavaScriptの変数として使いたかったんですが、 JavaSciptでは-は変数名に使えないので、 上のJavaScriptの中では更に-を消して変数名として使っています。

ホントはsocialCountという関数をfooterの中とかで1回だけ呼ぶようにすれば 一番良いのですが、 トップページなどでまとめて載せる事があったりする場合、 それだとダメなのでsharing_custom.htmlの中に一緒にいれておきました。

後は、source/_includes/article.htmlsource/_layouts/page.htmlsource/_layouts/post.html のそれぞれで

1
2
3
4
5
6
7
8
9
-{% unless page.sharing == false %}{% include post/sharing.html %}{% endunless %}
+{% unless page.sharing == false %}
+{% unless site.share_official == false %}
+{% include post/sharing.html %}
+{% endunless %}
+{% if site.share_custom == true %}
+{% include post/sharing_custom.html %}
+{% endif %}
+{% endunless %}

こんな感じでsharing.htmlsharing_custom.htmlを選べる様にしておきます。

最後にsource/_includes/after_footer.htmlの中で、 公式の方を呼ばない時に余計な物が読み込まれないように

source/_includes/after_footer.html
1
2
3
4
5
6
7
...
{% unless site.share_official == false %}
{% if site.hatena_button %}
a('//b.st-hatena.com/js/bookmark_button.js','hatebu-js');
{% endif %}
...
{% endunless %}

みたいな感じでshareボタンのスクリプトをまとめて share_officialで判断できる様に囲っておきます。 (これもシェアボタンを非同期読み込みでまとめて設定 の設定が前提の話です。)

この辺りはOctograyというテーマにも入れておいたのでそちらも参考にしてください。

追記: 2014/12/23

上の物だとはてなブックマークの数をhttpsのページで取ってこれないので 改良版を下に追記。

はてなブックマークの数取得をhttpsでも出来るようにする

追記ここまで

Sponsored Links
Sponsored Links

« JavaScriptだけでブログがコピーされた時にその内容をメールで送る ローカルでSSL(HTTPS)な接続を簡単に試せる様にする »

}