Ajax(pjax)遷移時におけるSNSボタン再構築
Updated / Published
HTML5のhistoryAPI(pushState)とAjaxを組み合わせた遷移(pjax)時に、Twitterのwidgets.jsで埋め込んだツイートボタンが更新されない事態に遭遇しました。pushStateでURLは変更できているにも関わらず、ツイートボタンを押しても取得されるタイトルとURLは最初にツイートボタンを表示したときの遷移前の状態であり、ごっそり入れ替えてもDOMが再構築されていない様子。
TwitterのAPIドキュメントを探すも、Ajax遷移時にDOMを再構築させるような使い方はどこにも載っていない。そこで、検索語を「Tweet button Ajax」に変えてみると、Cannot update tweet button with AJaxという記事がヒット、回答欄に解決策を提示してくれていました。
So, generate
<a class="twitter-share-button" href="http://twitter.com/share" data-url="http://example.com/post/12345">Tweet</a>
, insert it into the DOM, and then calltwttr.widgets.load()
, and everything should spring into life.
どうやらa
要素のボタンのコードを挿入してから、twttr.widgets.load()
を実行すれば、DOMを再構築してくれる模様。
以下、参考までに実際に書き換えたコードを記載しておきます。基本、何か挿入する必要があるものを<body>〜</body>
の最後尾にしていますが、これはAjaxでの切替対象外の部分であればどこでも構いません。なお、本解説のコードでは一部にjQueryを用いているので、書き換えが必要な際は適宜置き換えてください。
あわせて他のSNSボタンやDisqusのコメント欄についても記載しておきます。
Ajax(pjax)遷移時におけるツイートボタン再構築
まず、ジェネレータが生成する標準のコード。
<a href="https://twitter.com/share" class="twitter-share-button" data-lang="ja">ツイート</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
せっかくAjax遷移にするわけなので、widgets.jsは最初の読み込み時にのみ挿入して、あとはDOM再構築時にだけ呼び出すようにする必要があります。pjax遷移後に次のようなコードを評価させることで実現できます。
if(!window.twttr){
$('body').append('<a href="https://twitter.com/share" class="twitter-share-button" data-lang="ja">ツイート</a>');
var twitterjs = document.createElement("script");
twitterjs.async = true;
twitterjs.src = '//platform.twitter.com/widgets.js';
document.getElementsByTagName('body')[0].appendChild(twitterjs);
}else{
$('.twitter-share-button').replaceWith('<a href="https://twitter.com/share" class="twitter-share-button" data-lang="ja" data-url="' + encodeURI(location.href) + '" data-text="' + document.title + '">ツイート</a>');
twttr.widgets.load();
}
window.twttr
のオブジェクトがあるかないかをキーとします。window.twttr
が存在しなければ、<body>〜</body>
の最後尾にa
要素のボタンのコードを挿入してから、widgets.jsを生成します。これで最初の読み込み時のツイートボタンが展開されます。- 次にページ遷移した時には、
window.twttr
オブジェクトが存在しているので、以降は下段のコードが評価されることになります。さきほどのa
要素のボタンのコードはすでにツイートボタンとして<iframe class="twitter-share-button" ...>〜</iframe>
に展開されいるので、これを置換して、もとのa
要素のボタンのコードに戻します。 - 気をつけたいのが、必ず
data-url
とdata-text
に新しいページのURLとタイトルを付け足すことです。これがないとURLもタイトルもうまく書き換えてくれません。 - 新しいページ用の
data-url
とdata-text
を付け足したa
要素のボタンのコードに置き換えできたら、最後にtwttr.widgets.load();
を実行します。
Ajax(pjax)遷移時におけるfacebook いいね!ボタン再構築
これは公式サイトで検索すれば簡単に情報が見つかりました(FB.XFBML.parse)。FB.XFBML.parse();
を実行するようです。まず、ジェネレータが生成する標準のコードについて。
<div id="fb-root"></div>
<script>(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/all.js#xfbml=1&appId=ここにID";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div class="fb-like" data-href=" ここにURL" data-width="90" data-height="20" data-colorscheme="light" data-layout="button_count" data-action="like" data-show-faces="false" data-send="false"></div>
このall.jsは最初の読み込み時にのみ挿入して、あとはDOM再構築時にだけ呼び出すようにします。流れはTwitterのwidgets.js同様、pjax遷移後に次のようなコードを評価させることで実現できます。
if(!window.FB){
$('body').append('<div id="fb-root"></div><div class="fb-like" data-href="' + encodeURI(location.href) + '" data-width="90" data-height="20" data-colorscheme="light" data-layout="button_count" data-action="like" data-show-faces="false" data-send="false"></div>');
var fbjs = document.createElement("script");
fbjs.id = "facebook-jssdk";
fbjs.src = "//connect.facebook.net/ja_JP/all.js#xfbml=1&appId=ここにID";
document.getElementsByTagName('body')[0].appendChild(fbjs);
}else{
$('.fb-like').attr('data-href', encodeURI(location.href));
FB.XFBML.parse();
}
window.FB
のオブジェクトがあるかないかをキーとします。window.FB
が存在しなければ、<body>〜</body>
の最後尾に<div id="fb-root"></div>
と<div class="fb-like" ...></div>
をあわせて挿入してから、all.jsを生成します。これで最初の読み込み時のいいね!ボタンが展開されます。- 次にページ遷移した時には、
window.FB
オブジェクトが存在しているので、以降は下段のコードが評価されることになります。さきほどの<div class="fb-like" ...></div>
のdata-href
を新しいページのURLに書き換え、最後にFB.XFBML.parse();
を実行します。
Ajax(pjax)遷移時におけるはてなブックマークボタン再構築
これは公式のドキュメントもなければ、検索しても何ひとつヒットしませんでした。とりあえず、bookmark_button.jsの中身を見て、Hatena.Bookmark.BookmarkButton.setup();
で初期化してやるのが手っ取り早いと判断しました。まず、ジェネレータが生成するはてなブックボタンの標準のコードです。
<a href="http://b.hatena.ne.jp/entry/" class="hatena-bookmark-button" data-hatena-bookmark-layout="standard-balloon" data-hatena-bookmark-lang="ja" title="このエントリーをはてなブックマークに追加"><img src="http://b.st-hatena.com/images/entry-button/button-only@2x.png" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;" /></a><script type="text/javascript" src="//b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>
bookmark_button.jsを最初の読み込み時にのみ挿入し、ページ遷移後に都度Hatena.Bookmark.BookmarkButton.setup();
で初期化します。流れはTwitterのwidgets.js同様、pjax遷移後に次のようなコードを評価させることで実現できます。
if(!window.Hatena){
$('body').append('<a href="//b.hatena.ne.jp/entry/' + encodeURI(location.href) + '" class="hatena-bookmark-button" data-hatena-bookmark-title="' + document.title + '" data-hatena-bookmark-layout="standard-noballoon" data-hatena-bookmark-lang="ja" title="このエントリーをはてなブックマークに追加"><img src="//b.hatena.ne.jp/images/entry-button/button-only@2x.gif" alt="このエントリーをはてなブックマークに追加" width="20" height="20" /></a>');
var hatebujs = document.createElement("script");
hatebujs.async = true;
hatebujs.src = '//b.hatena.ne.jp/js/bookmark_button.js';
document.getElementsByTagName('body')[0].appendChild(hatebujs);
}else{
$('.hatena-bookmark-button-frame').replaceWith('<a href="//b.hatena.ne.jp/entry/' + encodeURI(location.href) + '" class="hatena-bookmark-button" data-hatena-bookmark-title="' + document.title + '" data-hatena-bookmark-layout="standard-noballoon" data-hatena-bookmark-lang="ja" title="このエントリーをはてなブックマークに追加"><img src="//b.hatena.ne.jp/images/entry-button/button-only@2x.gif" alt="このエントリーをはてなブックマークに追加" width="20" height="20" /></a>');
Hatena.Bookmark.BookmarkButton.setup();
}
window.Hatena
のオブジェクトがあるかないかをキーとします。window.Hatena
が存在しなければ、<body>〜</body>
の最後尾にa
要素のボタンのコードを挿入してから、bookmark_button.jsを生成します。これで最初の読み込み時のはてなブックマークボタンが展開されます。- 次にページ遷移した時には、
window.Hatena
オブジェクトが存在しているので、以降は下段のコードが評価されることになります。さきほどのa
要素のボタンのコードはすでにはてなブックマークボタンとして<iframe class="hatena-bookmark-button-frame" ...>〜</iframe>
に展開されいるので、これを置換して、もとのa
要素のボタンのコードに戻します。 href
とdata-hatena-bookmark-title
を新しいページのURLとタイトルにした状態のa
要素のボタンのコードに置き換えできたら、最後にHatena.Bookmark.BookmarkButton.setup();
を実行します。
Ajax(pjax)遷移時におけるDisqusコメント欄の再構築
これは公式のドキュメントがあります。Using Disqus on AJAX sitesを参照してください。pjax遷移後に以下のコードを実行するだけです。this.page.identifier
に一意のIDを、this.page.url
にURLを入れる必要があるのですが、URLも一意なので両方同じでも問題ありませんでした。
DISQUS.reset({
reload: true,
config: function () {
this.page.identifier = encodeURI(location.href);
this.page.url = encodeURI(location.href);
}
});
ここで紹介した以外のSNSボタンについても、やり方や手順はほとんど同じだと思われます。Disqusはさすがですね。最初からこういう使われ方を想定して設計されている点、非常に感心させられます。Twitterもfacebookも手立ては用意されているけど、イマイチ。はてなブックマークは。。。
なお、Ajax(pjax)遷移で読み込むことで、これらSNSボタンの遷移時の展開は格段に高速化できています。