Skip to content

Instantly share code, notes, and snippets.

@glebm
Last active October 12, 2018 15:36
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save glebm/2496daf445877055447a6fac46509d9a to your computer and use it in GitHub Desktop.
Save glebm/2496daf445877055447a6fac46509d9a to your computer and use it in GitHub Desktop.
Universal Rails async onPageLoad script (Turbolink v2, v5.0, v5.1, jquery-turbolinks, and no Turbolinks)

This lets you load application JavaScript asynchronously from <head>. Compatible with Turbolink v2, v5.0, v5.1, jquery-turbolinks, and no Turbolinks. Compatible with all browsers and IE9+ (only IE10+ will actually benefit from this though).

How to use this:

  1. Add the HTML snippet just before the closing </body> tag.

  2. Add the on_page_load.js script to the beginning of your application.js, and use window.App.onPageLoad(...) instead of document.addEventListener('DOMContentLoaded', ...) and jQuery(($) -> ...) everywhere.

  3. Include your JavaScript like so:

    <head>
      ...
      <%= javascript_include_tag 'application',
                                  # In debug mode Rails will serve individual script files.
                                  # With `async: true` these would be loaded out-of-order.
                                  # Disable `async` in assets debug mode:
                                  async: !Rails.application.config.assets.debug,
                                  defer: true,
                                  'data-turbolinks-track': 'reload' %>
    </head>
<%# If the app JS is loaded via an [async] script, the JS may
run before or after DOMContentLoaded. Expose a flag, so it can
initialize correctly. %>
<script data-turbolinks-eval="false">
document.addEventListener('DOMContentLoaded', function() {
(window.App = window.App || {}).DOMContentLoadedFired = true;
});
</script>
(function() {
this.App = this.App || {};
var App = this.App;
var isTurbolinks = 'Turbolinks' in window && window.Turbolinks.supported;
var isTurbolinks5 = isTurbolinks && 'clearCache' in window.Turbolinks;
var onPageLoadFiredOnce = false;
var pageLoadCallbacks = [];
var triggerOnPageLoad = function() {
pageLoadCallbacks.forEach(function (callback) {
callback();
});
onPageLoadFiredOnce = true;
};
// Fires the callback on DOMContentLoaded or a Turbolinks page load.
// If called from an async script on the first page load, and the DOMContentLoad event
// has already fired, will execute the callback immediately.
App.onPageLoad = function(callback) {
pageLoadCallbacks.push(callback);
// With async script loading, a callback may be added after the DOMContentLoaded event has already triggered.
// This means we will receive neither a DOMContentLoaded event, nor a turbolinks:load event on Turbolinks 5.
if (!onPageLoadFiredOnce && App.DOMContentLoadedFired) {
callback();
}
};
if (isTurbolinks5) {
// In Turbolinks 5.0.1, turbolinks:load may have already fired (before DOMContentLoaded).
// If so, add our own DOMContentLoaded listener:
// See: https://github.com/turbolinks/turbolinks/commit/69d353ea73d10ee6b25c2866fc5706879ba403e3
if (window.Turbolinks.controller.lastRenderedLocation) {
document.addEventListener('DOMContentLoaded', function() {
triggerOnPageLoad();
});
}
document.addEventListener('turbolinks:load', function () {
triggerOnPageLoad();
});
} else {
// Turbolinks Classic (with or without jQuery.Turbolinks), or no Turbolinks:
if (!App.DOMContentLoadedFired) {
document.addEventListener('DOMContentLoaded', function () {
triggerOnPageLoad();
});
}
if (isTurbolinks) {
document.addEventListener('page:load', function () {
triggerOnPageLoad();
})
}
}
}();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment