Search code examples
ruby-on-rails-5turbolinks

Turbolinks invokes "load" scripts just before load content of next page


Having:
- page A is pure HTML no scripts;
- page B having some Javascript on Turbolinks "turbolinks:load" event.

Loading page B and having scripts correctly running on "turbolinks:load". Now I am on page B and click link to page A. This time scripts from "turbolinks:load" fired just before loading of page A content.

Is it a bug or some defined behavior?

Example simple Rails code to reproduce.
Controller:

#app/controllers/turbobugs_controller.rb
class TurbobugsController < ApplicationController
  def a
    render layout: false
  end

  def b
  end
end

Routes:

namespace :turbobugs do
  get :a
  get :b
end

Views:

#app/views/turbobugs/a.html.haml
%h1
  No scripts here
= link_to turbobugs_b_path, turbobugs_b_path

 

#app/views/turbobugs/b.html.haml
= link_to turbobugs_a_path, turbobugs_a_path
%script
  document.addEventListener("turbolinks:load", function() { console.log('I am script in page B running on window.location: ' + window.location); });

Opening page B and see in console:

"I am script in page B running on window.location: http://localhost:3000/turbobugs/b"

Then follow link to page A and see in log:

"I am script in page B running on window.location: http://localhost:3000/turbobugs/a"

Why :) ?

Also please note, that script starts running really on page B content, but terminated (if takes too long or some Ajax async) just before A loaded (or start loading or whatever...).


Solution

  • This is defined behaviour. Assuming your asset tags (JS or CSS) have data-turbolinks-track="reload" attributes on them, Turbolinks fully reloads the page when it notices the absence of the scripts on page A. According to the Turbolinks README:

    This ensures that users always have the latest versions of your application’s scripts and styles.

    You can change this behaviour by removing data-turbolinks-track="reload" from your asset tags, but be aware of the above.

    In terms of the observed logging behaviour, Turbolinks goes through a particular procedure for every visit. For this case it can be summarised roughly as:

    1. Request the new content
    2. Change the URL
    3. Check any differences in tracked elements
    4. Fully reload if there are differences or render the new content if not
    5. Run complete functions

    Part of the complete process is to trigger turbolinks:load, and even though the page is reloaded, the event is still triggered just before this actually happens.


    As a side note, when using Turbolinks, adding event listeners to inline scripts can lead to some unexpected behaviour.

    Traditionally, inline scripts have indicated that the code will only execute on the given page—event listeners will be destroyed when a user navigates from one location to the next. This is not the case when using Turbolinks—event listeners hang around unless they are removed.

    For example, if you add an event listener for turbolinks:load in an inline script, it will execute on every subsequent page load unless removed. What's more, if the user revisits the page with the inline script, the listener will be bound again, resulting in the handler being called multiple times.