おおいしつかさ


旅行とバイクとドライブと料理と宇宙が好き。
Ubie Discoveryのプログラマ。
Share:  このエントリーをはてなブックマークに追加

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

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が表示されます。