at posts/single.html

ソーシャルボタンプラグインを遅延ロード対応

tDiaryを月表示したときにソーシャルボタンプラグインが固まるという報告があった。 日記のセクション数だけ外部のJavaScriptファイルを読み込むため、セクション数が多くなるとソーシャルボタンの表示に時間がかかるためだろう。 対応策は以下3つ。

  1. 月表示ではソーシャルボタンを無効にする
  2. 手動でソーシャルボタンを表示できるようにする
  3. 遅延ロードで順番にボタンを表示する

1は簡単に対応できる。 でも、月表示でエントリをまとめて読むときにもブクマ数を知りたいので、できれば避けたい。 2は読み手側に負担を強いるので無い。

ということで、3の遅延ロードで対応することにした。 たださんが amazon.js でも遅延ロードしていると教えてくれたので、ソースを読む。

	$(window).bind('scroll', function(event){
		var bottom = $(window).height() + $(window).scrollTop();
		$('a[href*="://www.amazon.co.jp/"]').each(function(){
			var a = $(this);
			if (bottom > a.offset().top){
				shorten(a);
			}
		});
	});

思ったよりもシンプル。画面の表示位置と要素の位置を比較して、画面内か画面より上の要素を対象にしている。 これをスクロールイベントにバインドすることで、画面に表示される要素が順に処理されていくのか。

ソーシャルボタンプラグインでも、amazon.jsを参考にしながら遅延ロードの処理を実装した。

  function socialbutton(target) {
    var bottom = $(window).height() + $(window).scrollTop();
    $($tDiary.blogkit ? '.day' : '.section')
      .filter(function() {
        return bottom > $(this).offset().top;
      })
      .filter(function() {
        return $(this).find('.socialbutton').size() == 0
      })
      .each(function() {
        // ソーシャルボタンを表示する処理が続く
      });
  }

まずdiv.section(BlogKitの場合はdiv.day)の一覧を取ってきて、画面内に含まれてかつボタンを表示していない要素だけをfilter関数で抽出している。 こうやってメソッドチェーンで簡潔に書けるのが jQuery のいいところだね。

あとはこの関数をscrollイベントにバインドしている。 無名関数にしていないのは、起動直後にもボタンを表示したいから。

  $(window).bind('scroll', function(event) {
    socialbutton(document);
  });

  socialbutton(document);

Chromeブラウザで試したところ、一回のスクロールでscrollイベントが大量(10〜30)に発行されているのが分かった。 もし処理が重くなるようなら、スクロール量を測っておき、例えば200px移動したときにsocialbutton関数を呼び出すなどの処理を入れれば良さそう。 今のところ、CPU使用率がそれほど変わらなかったので、特に対処はしていない。

Google+ボタンが表示されない問題への対処

もう一つ別の問題があって、遅延ロード対応するとGoogle+ボタンが最初の1回目しか表示されなくなった。 調べてみたら、これはボタンの呼び出し方が原因だった。 普通はGoogle+ボタンの呼び出しは以下のようになる。

まず、ボタンを表示する位置にplusone要素を配置。

   <g:plusone></g:plusone>

次にスクリプトを呼び出す。

    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">

jQueryのsocialbuttonプラグインでは、socialbutton関数を呼び出したときに内部でこの処理をやってくれている。 でも、遅延ロードのためにsocialbutton関数を2回以上呼び出す場合は、この方法では対応できない。 これはGoogle+ボタン側の仕様らしい。

代わりにどうしたかというと、以下の処理にした。

1回目の呼び出しでは普通にjsファイルを読み込む。

    <script type="text/javascript" src="https://apis.google.com/js/plusone.js">

2回目以降の呼び出しでは、jsファイルを読み込むのではなく、gapi.plusone.go()関数を実行する。

  gapi.plusone.go()

これは、Google+ボタンのドキュメントに書かれている明示的な読み込みの手順になる。 jQueryのソーシャルボタンプラグインに、このパッチを当てることで、月表示モードでもGoogle+ボタンが表示されるようになった。

まとめ

遅延ロードを実装することで、月表示モードでもブラウザが固まらなくなった。 遅延ロードのような技が使えるのは、JavaScriptでプラグインを書いた恩恵だなぁ。

関連する日記