animate page:change with turbolinks

Something like this might work (in CoffeeScript using jQuery):

$(document).on 'page:fetch', ->
  $('#content').fadeOut 'slow'

$(document).on 'page:restore', ->
  $('#content').fadeIn 'slow'

You could also use CSS animations or some more complicated JavaScript. Here's a great post that goes in-depth with some examples and demos.

Also, I came across a turbolink transitions ruby gem (which I have not tested).


You can't really update just one part of the window with turbolinks, you may want to use pjax instead. But you could do some tricky things to make it look like it's transitioning and updating only part of the page with turbolinks.

I have an index page that lists some items, and if you click an item it goes to the show page. But from the show page I still want to be able to navigate directly to other items' show pages. On the index page the list of items is six columns wide, and I animate it to be narrower on the show page to make room for the details.

// items.css.sass
.width-trans
  +transition(width 400ms ease-in)

Define a simple CSS class to transition changes to the width of the element.

<!-- index.html.erb ->
<div id="target_div" class="width-trans six columns">
  <%= link_to "Item",
              item_path(@item.id),
              :'data-no-turbolink' => true,
              :class => 'trans' %>
</div>

We set data-no-turbolink to true on the links we're transitioning from so we can have a little more control.

# items.js.coffee
$(document).on 'page:restore', (e) ->
  if window.location.href.match(/\/items\/\d$/) || window.location.href.match(/\/items$/)
    $("#target_div").removeClass("three").addClass("six")
  else
    $("#target_div").removeClass("six").addClass("three")

target_page = undefined  # Sorry, this 'target_page' stuff is a little janky.

if Modernizr.csstransitions
  eventEndNames =
    'WebkitTransition': 'webkitTransitionEnd'
    'MozTransition':    'transitionend'
    'OTransition':      'oTransitionEnd'
    'msTransition':     'MSTransitionEnd'
    'transition':       'transitionend'

  eventName = eventEndNames[Modernizr.prefixed('transition')]

  $(document).on eventName, '#target_div', () ->
    Turbolinks.visit(target_name) if target_page
    target_page = undefined

$(document).on 'click', '.trans', (e) ->
  e.preventDefault()

  target_name = e.target.href
  $(e.target).closest("#target_div").toggleClass("three six")
  false

Okay, this is a little crazy and maybe we can figure out a more general way of doing this. But here's the run-down on what's going on:

We've disabled Turbolinks on our .trans links so that Turbolinks doesn't instantly fetch the new page (we'll explicitly tell Turbolinks when to fetch it). When we click a .trans link it looks up the #target_div and toggles the .six and .three classes. I use Foundation, so three and six are part of my grid system and define the width of my divs.. so when I change from six to three this changes the width.

That triggers my CSS transition, #target_div reduces in width and then it fires off the transition end event. When the transition ends we call Turbolinks.visit() on our target URL.

The #target_div should be rendered by both the index and show actions. We have the part listening to page:restore which sets #target_div's columns to six or three depending on which action is rendering it.

The end result is that #target_div appears to animate as we're changing pages, but in reality it's transitioning before we change pages and both pages render the same div just at its different states.