light log

学んだこととか

RailsのTurbolinks

Railsの勉強でWebアプリを作っていて、Turbolinksの存在をよく知らなかったために、ページ遷移したときにloadイベントが発生せずに困った話。

事象

今日は「トップに戻る」ボタンをjQueryを使って付けてみた。ある程度以上スクロールすると表示されるやつ。よく見かけるやつ。

これは検索すればいくらでもサンプルが出てくるし、問題なく付けることができた。しかし、いろいろと動作確認しているうちに、どうも挙動がおかしいことに気付いた。

ページをリロードしたときにはちゃんと動作するのに、アプリ内の別のページから遷移してきたときにはうまく動いていない。どうやら、loadイベントのハンドラが実行されていないように見える。

それで調べてみると、Rails 4から導入されてデフォルトで有効になっているTurbolinksという機能が、アプリ内のページ遷移を基本的にすべてAjax通信+DOMの更新に置き換えているとのこと。これによってアセットの再ダウンロードを省略できるなどのメリットがあるらしい。

どうやらこれのせいでページ遷移がなくなってloadイベントが呼ばれなくなってるようだ。

Turbolinksについてはちゃんとは知らなかったけど、名前は何度か目にしてたし、なんとなく今回の犯人ではないかと検討はついてた。

対策

対策としては、Turbolinksを無効にするか、TurbolinksがページのDOMを書き換えたタイミングでコードを実行するか。

受けられる恩恵はできるだけ受けたいので、後者にする。

Turbolinksはpage:changeイベントを提供していて、これがまさにページが切り替わったときに発生するイベントらしい。

これを使えばいいんだけど、手元の本では、

$(document).ready(init)
$(document).on('page:change', init)

としていて、イディオムだと書いてある。

でも、上の行は必要なのかが気になった。page:changeだけで初回ロードも対応できないものだろうか?

TurbolinksのReadmeを見てみる。

page:change the page has been changed to the new version (and on DOMContentLoaded)

page:change the page has been changed to the new version (and on DOMContentLoaded)

初回でもいけるのかはわからない。

Turbolinksのソースを見てみる。以下が抜粋。

...

installDocumentReadyPageEventTriggers = ->
  document.addEventListener 'DOMContentLoaded', ( ->
    triggerEvent EVENTS.CHANGE
    triggerEvent EVENTS.UPDATE
  ), true

...

browserSupportsCustomEvents =
  document.addEventListener and document.createEvent

...

if browserSupportsCustomEvents
  installDocumentReadyPageEventTriggers()
  installJqueryAjaxSuccessPageUpdateTrigger()

turbolinks/turbolinks.js.coffee at master · rails/turbolinks

EVENTS.CHANGEは'page:change'と定義されている)

これを見る限り、document.addEventListenerdocument.createEventがサポートされていれば、DOMContentLoadedのタイミングでpage:changeが発生するため、初回ロード時にも対応できそう。

document.addEventListenerdocument.createEventIE9以上であればサポートされているようなので、とりあえず今回はそれでOKとする。

ということで、

$ ->
    doSomething()

としていたところを

$(document).on 'page:change', ->
    doSomething()

に変えただけで済んだ。

まとめ

本当に今回の対応でよかったのかいまいち確信持てない。

あとRailsは裏で勝手にいろいろやってくれていて、それぞれ意味があってやってくれているんだろうけど、ちゃんと理解していないと思わぬ挙動に遭遇してビクッとする。

Ruby on Rails 4 アプリケーションプログラミング

Ruby on Rails 4 アプリケーションプログラミング