Rails3.2 + pjax を使ってみた。+ pjaxでのgoogle adsense

Tsukasa OISHI

kaeruspoon のデザイン変更とあわせてRails3.2.2化 + pjax対応をしてみました。
pjax対応は、ライブラリが用意されているので思いの外簡単です。今のところ、DHHがつくった pjax_railsと、Rackに組み込んで使う rack-pjaxが有名なようです。

pjax_railsの使い方は簡単です。READMEにあるとおりですが、まずGemfileで設定しておきます。

gem 'pjax_rails'

それから、app/assets/javascripts/application.jsにてjqueryの後くらいにpjaxを追加します。

//= require jquery
//= require jquery_ujs
//= require pjax
//= require_tree .

あとはviewsのlayoutの中で、yield :contentしている部分を

<div data-pjax-container>

でくくります。

<html>
 ...
  <div data-pjax-container>
    <%= yield %>
  </div>

これで完了です。リンクをクリックすると、Railsはcontentのレンダリングだけを行ってレスポンスとして返します。ブラウザ側は受け取ったデータを、data-pjax-container内に差し替えます。
pjax_railsはとても簡単なのですが、デフォルトですべてのリンクがpjax化されたり、titleタグが変化しなかったりします。

kaeruspoonではrack-pjaxを使うことにしました。こちらは明示的にpjaxを行いたいリンクを指定できるのと、rack内で処理するためRailsの挙動は変わらなかったりするのでわかりやすいです。
具体的には、Railsは通常どおりlayoutまで含めてレンダリングしますが、Rack内でhpricotにより必要な部分を抜き出してレスポンスとします。このとき、titleタグもうまいことやってくれるので、コードの修正なしにtitleタグも差し替えることが可能です。

Gemfileに以下を追加します。

gem 'rack-pjax

Rack::PjaxをRackのミドルウェアに積みます。
config/application.rb

module Kaeruspoon
  class Application < Rails::Application
    config.middleware.use Rack::Pjax
    ....
  end
end

jquery-pjaxを別途app/assets/javascriptsなどに配置しておきます。
それから、app/assets/javascripts/application.jsにてjqueryの後くらいにjquery-pjaxを追加します。

//= require jquery
//= require jquery_ujs
//= require jquery.pjax
//= require_tree .

pjax_railsと同様に差し替えを行いたい箇所を決定しておきます。同様にdata-pjax-containerタグとするなら、pageロード後に以下のようなjavascriptを実行するようにします。

$('a:not([data-remote]):not([data-behavior]):not([data-skip-pjax])').pjax('[data-pjax-container]')

ここで対象となるリンクが指定されます。kaeruspoonでは、

$(".pjax a").pjax('[data-pjax-container]');

というように、class名pjaxのDOM内のリンクを対象にするようにしました。

pjaxでコンテンツを差し替えすると、ブラウザのスクロール位置が変になることがあります(縦に長いページのあとに縦に短いページを呼んだときなど)。そのため、pjaxによる差し替えが成功したあとは、ページのトップにスクロールするようにしてみました。

  $('[data-pjax-container]').on('pjax:success', function(){
    $('html,body').animate({ scrollTop: 0 }, 'slow','swing');
    articleLoaded();
  });

このあたりのイベントハンドリングはjquery-pjaxでいろいろ定義されています。

pjaxを採用してひとつ問題になったのがgoogle adsenseです。adsense内でdocument.wrtie()などをコールしているため、古いブラウザなどだと広告だけが存在する白紙ページになってしまいます(最近のブラウザなら広告のほうが表示されないようになるみたい)。
javascriptで動的に表示を変更したページでのadsenseの表示方法がよくわかりませんでした。iframeを使うのは規約違反のようです。また、 ページロード後に外部スクリプトのdocument.writeを実行する方法の間違いを直すなどのようにdocument.write自体をオーバーライドする手法もありますが、こちらも規約違反になりそうです。
なので今回は、pjaxでの遷移先ではadsenseの表示を諦めました。

<% if params[:_pjax].blank? %>
  ...adsense表示
<% end %>

これでpjax以外の場合のみ、adsenseが表示されます。