はてなブログ/軽量・高速ソーシャルボタン・シェアボタン改訂版(各記事に複数表示が可能)

はてなブログ/軽量・高速ソーシャルボタン・シェアボタン改訂版(各記事に複数表示が可能)

下記記事にて、はてなブログ用の軽量・高速自作ソーシャルボタンを公開していますが、公式ボタンのように記事の上下に入れられないかとのリクエストをいただきましたので改訂版を公開します。

www.imuza.com

改訂版では、デザイン > カスタマイズ > 記事 > ソーシャルパーツ表示設定

  • 記事上下に表示
  • 記事下にのみ表示(デフォルト)

が有効になり、記事上下にソーシャルボタンを表示できます。

このラジオボタンは、「記事上下に表示」にチェックを入れますと、記事ボトムと同様の<div class="social-buttons"></div>が記事タイトルの下に挿入されますので、そこにも自作ソーシャルボタンを入れるよう変更しました。

もし、挿入位置を変えたい場合は、「デフォルト」にチェックをし、記事内の好きなところに<div class="social-buttons"></div>を入れておけば、そこにソーシャルボタンが表示されます。

導入方法は、上記リンク記事の繰り返しになりますが、Javascript のみ変更した記事を下記に掲載しました。

概要

はてなブログの読み込みが重い理由のひとつは、ソーシャルボタンがiframeで読み込まれているからです。以下5回の記事で、hatena, facebook, twitter, google+ をダイレクトリンクに変更し、シェアカウントを素の Javascript 非同期で読み込む方法を試してきましたが、何とか完成しましたので公開します。

  1. シェアボタン(ソーシャルボタン)を改良して読み込みを速くする
  2. Facebookシェアボタン/シェアカウントを素の Javascript で取得する
  3. Facebookシェアダイアログでシェアする/Facebook SDK for JavaScript
  4. ツイート数付きツイートボタンをオリジナルでつくる(素の javascript 付き)
  5. Google+のシェアボタンをカウント付きで自作する

ボタンの見た目

ボタンの見た目は、 CSSで自由に変更できます。デフォルトは次の通りです。

スマートフォンの場合

現在のところ、レスポンシブデザインのテーマを使用していない場合はスマートフォンでは表示されません。はてなブログのスマートフォン向けテーマではトップ画面は記事一覧でソーシャルボタンは表示されませんすので、記事表示の場合に自作のものを表示するよう、記事の記事下やフッタに入れて、CSS をどうするかを考えればいけるとは思います。

レスポンシブデザインのテーマを使用している場合は次のように表示されます。

挿入コード(Javascript 圧縮版)

必須項目

Facebook と Twitter のシェアカウントを取得するために次の2つの登録が必要です。

  1. Facebook アプリID
    【はてなブログ高速化3】Facebookシェアダイアログでシェアする/Facebook SDK for JavaScript – IMUZA.com」を参考にアプリID を取得し、以下のコードの26行目「Facebook のアプリID」を自分のアプリID に変更してください。
  2. count.jsoon への登録
    widgetoon.js & count.jsoon | digitiminimi」でサイトを登録してください。

上記完了後、下のコードをカスタマイズ > フッタに入れてください。

<ul id="tmplShareButtons">
  <li class="hatena">
    <a class="hatena-bookmark-button" data-hatena-bookmark-layout="simple">
      <span class="hatena-bookmark-count share-count"></span>
      <i class="blogicon-bookmark lg"></i><span class="share-label">ブックマーク</span></a>
  </li>
  <li class="facebook">
    <a href="javascript:void(0)" class="facebook-share-button"> 
      <span class="facebook-count share-count"></span>
      <i class="blogicon-facebook lg"></i><span class="share-label">シェア</span></a>
  </li>
  <li class="twitter">
    <a class="twitter-button">
      <span class="twitter-count share-count"></span>
      <i class="blogicon-twitter lg"></i><span class="share-label">ツイート</span></a>
  </li>
  <li class="googleplus">
    <a class="googleplus-button">
      <span class="googleplus-count share-count"></span>
      <i class="blogicon-plus lg"></i>
      <svg version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" width="36px" height="22px" viewBox="-14 -8 72 44" class="u7 uzlpSb"><path d="M32 8.2h-3v4h-4V15h4v4h3v-4h4v-2.9h-4V8.2zm6-2V8h3v15h3V4l-6 2.2z"></path><path d="M11.4 11.3v4.5h6c-.4 2.6-2.7 4.5-6 4.5-3.6 0-6.6-3.1-6.6-6.7s2.9-6.7 6.6-6.7c1.7 0 3.1.5 4.3 1.7l3.2-3.2c-2-1.8-4.5-2.9-7.5-2.9C5.3 2.5.3 7.5.3 13.6s5 11.1 11.1 11.1c6.5 0 10.7-4.6 10.7-10.9 0-.8-.1-1.7-.2-2.5H11.4z"></path></svg></a>
  </li>
</ul>
<script>
  window.fbAsyncInit = function() {
    FB.init({appId:'Facebook のアプリID',xfbml:true,version:'v2.7'});
  };
  (function(d, s, id){
     var js, fjs = d.getElementsByTagName(s)[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement(s); js.id = id;
     js.src = "//connect.facebook.net/ja_JP/sdk.js";
     fjs.parentNode.insertBefore(js, fjs);
   }(document, 'script', 'facebook-jssdk'));
</script>


<script>
!function(){var t=function(t,e){var n="https://clients6.google.com/rpc?key=AIzaSyCKSbrvQasunBoV16zDH9R33D88CeLr9gQ",o=new XMLHttpRequest;o.onreadystatechange=function(){if(4===o.readyState&&200===o.status){var t=JSON.parse(o.responseText);e(t.result.metadata.globalCounts.count)}else e(0)},o.open("post",n,!0),o.setRequestHeader("Content-Type","application/json"),o.send(JSON.stringify(t))},e=function(t,e,n){var o=document.createElement("script");o.type="text/javascript";var a="ExternalCallback_"+e;window[a]=function(t){o.parentNode&&o.parentNode.removeChild(o);try{delete window[a]}catch(e){window[a]=null}n(t)},o.src=t+"&callback="+a,document.body.appendChild(o)},n=function(t,e){var n=new XMLHttpRequest;n.open("get",t,!0),n.onreadystatechange=function(){if(4===n.readyState&&200===n.status){var t=JSON.parse(n.responseText),o="share"in t?t.share:{},a="share_count"in o?o.share_count:0;e(a)}else e(0)},n.send(null)},o=0,a=document.getElementById("tmplShareButtons");a.removeAttribute("id"),Array.prototype.forEach.call(document.getElementsByClassName("hentry"),function(s){var r=s.getElementsByClassName("bookmark")[0],i=r.getAttribute("href"),c=r.textContent;Array.prototype.forEach.call(s.getElementsByClassName("social-buttons"),function(s){var r=a.cloneNode(!0),l=r.getElementsByClassName("hatena-bookmark-button")[0];l.setAttribute("href","http://b.hatena.ne.jp/entry/"+i),l.setAttribute("data-hatena-bookmark-title",c);var u="http://api.b.st-hatena.com/entry.count?url="+encodeURIComponent(i);e(u,o,function(t){l.getElementsByClassName("hatena-bookmark-count")[0].textContent=t}),o++;var p=r.getElementsByClassName("facebook-share-button")[0];p.addEventListener("click",function(){FB.ui({method:"share",href:i},function(){})});var u="https://graph.facebook.com/?id="+encodeURIComponent(i);n(u,function(t){p.getElementsByClassName("facebook-count")[0].textContent=t});var m=r.getElementsByClassName("twitter-button")[0];m.setAttribute("href","http://twitter.com/intent/tweet?url="+encodeURIComponent(i)+"&text="+c);var u="http://jsoon.digitiminimi.com/twitter/count.json?url="+encodeURIComponent(i);e(u,o,function(t){m.getElementsByClassName("twitter-count")[0].textContent=t.count}),o++;var d=r.getElementsByClassName("googleplus-button")[0],h=500,g=500,y=(window.screen.width-h)/2,f=(window.screen.height-g)/2;d.setAttribute("href","javascript:void(window.open('https://plus.google.com/share?url="+encodeURIComponent(i)+"', '_blank', 'width="+h+",height="+g+",left="+y+",top="+f+"'))");var C={method:"pos.plusones.get",id:"p",params:{nolog:!0,id:i,source:"widget",userId:"@viewer",groupId:"@self"},jsonrpc:"2.0",key:"p",apiVersion:"v1"};t(C,function(t){d.getElementsByClassName("googleplus-count")[0].textContent=t}),s.appendChild(r)})}),a.parentNode.removeChild(a)}();
</script>

CSS

以下のコードをカスタマイズ > デザインCSSに入れてください。

.social-buttons ul {
  padding: 0;
  margin: 0;
}
.social-buttons ul li {
  display: inline-block;
  margin: 0;
  padding: 0;
  list-style-type: none;
  width: 15%;
  border-radius: 3px;
  vertical-align: text-top;
}
.social-buttons ul li a {
  width: 100%;
  display: inline-block;
  color: #fff;
  text-decoration: none;
  position: relative;
  text-align: center;
}
.social-buttons ul li a span.share-count {
  display: inline-block;
  width: 100%;
  color: #000;
  background: #fff;
  line-height: 2rem;
  border-radius: 3px 3px 0 0;
  opacity: 1;
  -webkit-transition: opacity 0.4s, transform 0.4s;
  transition: opacity 0.4s, transform 0.4s;
}
.social-buttons ul li a i {
  padding: 0 5px;
}
.social-buttons ul li a i.blogicon-plus {
  color: #dd5144;
}
.social-buttons ul li a svg {
  position: absolute;
  left: 0;
  right: 0;
  margin: 0 auto;
}
.social-buttons ul li a svg path {
  fill: #fff;
}
.social-buttons ul li a span.share-label {
  font-size: .7rem;
}
.social-buttons ul li a:hover span.share-count {
  opacity: 0.8;
}
.social-buttons ul li.hatena {
  background: #00a4de;
  border: solid 1px #00a4de;
}
.social-buttons ul li.facebook {
  background: #3e59a5;
  border: solid 1px #3e59a5;
}
.social-buttons ul li.twitter {
  background: #1b95e0;
  border: solid 1px #1b95e0;
}
.social-buttons ul li.googleplus {
  background: #dd5144;
  border: solid 1px #dd5144;
}
@media (max-width: 767px) {
  .social-buttons ul li a span.share-label {
    display: none;
  }
}

これで完了です。

このサイトの場合はいろいろ DOM 操作をやっていますのでもともとやや重めですが、このソーシャルボタンに変更したら、8秒くらいかかっていた読み込みが4秒くらいになりました。

以下は、Javascript の圧縮前のソースです。

Javascript 可読版

/*--------------------------------------------------------------------------*
 *
 *  はてなブログ用ソーシャルボタン
 *
 *  Copyright (c) 2016 IMUZA.com http://www.imuza.com
 *  Released under the MIT license
 *  http://opensource.org/licenses/mit-license.php
 *  
 *--------------------------------------------------------------------------*/


(function(){
  var getGoogleShareCount = function(obj, callback) {
    var url = 'https://clients6.google.com/rpc?key=AIzaSyCKSbrvQasunBoV16zDH9R33D88CeLr9gQ';
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var obj = JSON.parse(xhr.responseText);
        callback(obj.result.metadata.globalCounts.count);
      } else {
        callback(0);
      }
    };
    xhr.open('post', url, true);
    xhr.setRequestHeader( 'Content-Type', 'application/json' );
    xhr.send(JSON.stringify(obj));
  };


  var getCountByJSONP = function(url, count, callback) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    var callbackName = 'ExternalCallback_' + count;
    window[callbackName] = function (data){
      if(script.parentNode){
        script.parentNode.removeChild(script);
      }
      try{
        delete window[callbackName];
      }catch(e){
        window[callbackName] = null;
      }
      callback(data);
    };
    script.src = url + '&callback=' + callbackName;
    document.body.appendChild(script);
  };


  var getFbShareCount = function(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('get', url, true);
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var obj1 = JSON.parse(xhr.responseText),
          obj2 = 'share' in obj1 ? obj1.share : {},
            count = 'share_count' in obj2 ? obj2.share_count : 0;
        callback(count);
      } else {
        callback(0);
      }
    };
    xhr.send(null);
  };


  var count = 0;
  var tmpl = document.getElementById('tmplShareButtons');
  tmpl.removeAttribute('id');


  Array.prototype.forEach.call(document.getElementsByClassName('hentry'), function(article) {
    var bm = article.getElementsByClassName('bookmark')[0];
    var permalink = bm.getAttribute('href');
    var title = bm.textContent;
    
    Array.prototype.forEach.call(article.getElementsByClassName('social-buttons'), function(sb){
      var clone = tmpl.cloneNode(true);


      var hatenaButton = clone.getElementsByClassName('hatena-bookmark-button')[0];
      hatenaButton.setAttribute('href', 'http://b.hatena.ne.jp/entry/' + permalink);
      hatenaButton.setAttribute('data-hatena-bookmark-title', title);    
      var url = 'http://api.b.st-hatena.com/entry.count?url=' + encodeURIComponent(permalink);
      getCountByJSONP(url, count, function(data) {
        hatenaButton.getElementsByClassName('hatena-bookmark-count')[0].textContent = data;
      });
      count++;


      var facebookButton = clone.getElementsByClassName('facebook-share-button')[0];
      facebookButton.addEventListener('click', function() {
        FB.ui({
          method: 'share',
          href: permalink,
        }, function(response){});
      });
      var url = 'https://graph.facebook.com/?id=' + encodeURIComponent(permalink);
      getFbShareCount(url ,function(data) {
        facebookButton.getElementsByClassName('facebook-count')[0].textContent = data;
      });


      var twitterButton = clone.getElementsByClassName('twitter-button')[0];
      twitterButton.setAttribute('href', 'http://twitter.com/intent/tweet?url=' + encodeURIComponent(permalink) + '&text=' + title);
      var url = 'http://jsoon.digitiminimi.com/twitter/count.json?url=' + encodeURIComponent(permalink);
      getCountByJSONP(url, count, function(data) {
        twitterButton.getElementsByClassName('twitter-count')[0].textContent = data.count;
      });
      count++;


      var googleButton = clone.getElementsByClassName('googleplus-button')[0];
      var w = 500, h = 500, x = (window.screen.width - w ) / 2, y = (window.screen.height - h) / 2;
      googleButton.setAttribute('href', "javascript:void(window.open('https://plus.google.com/share?url=" + encodeURIComponent(permalink) + "', '_blank', 'width=" + w + ",height=" + h + ",left=" + x + ",top=" + y + "'))");
      var obj = {
        "method":"pos.plusones.get",
        "id":"p",
        "params":{
          "nolog":true,
          "id":permalink,
          "source":"widget",
          "userId":"@viewer",
          "groupId":"@self"
        },
        "jsonrpc":"2.0",
        "key":"p",
        "apiVersion":"v1"
      };
      getGoogleShareCount(obj, function(data) {
        googleButton.getElementsByClassName('googleplus-count')[0].textContent = data;
      });


      sb.appendChild(clone);
    
    });


  });
  tmpl.parentNode.removeChild(tmpl);
  
})();