I migrated my application to Ruby on Rails 5 (v5.2.2) and I'm trying to use Turbolinks 5 (v5.2.0).
In my application I have view files that include <script> elements and I would like to make them to properly work with Turbolinks (whose documentation states to avoid script elements and use the turbolinks:load event instead). What I have to make?
That is, for example, I have:
# app/views/.../_partial_template.html.erb
<script type="text/javascript">
$(document).ready(function () {
$.ajax({...})
});
// Note: I tried to use $(document).on('turbolinks:load', function() {...});
// and it seems that the JS code runs twice (or more) on every page load.
// I also tried to use <script type="text/javascript" data-turbolinks-eval="false">...</script>
// without success.
</script>
How should I refactor the above code to make it to properly work with Turbolinks?
Bonus:
The Turbolinks documentation says: You can use inline body scripts to set up per-page JavaScript state or bootstrap client-side models. What do they mean with "per-page JavaScript state" and "bootstrap client-side models"?
If your inline scripts are included at the bottom of the
<body>(or if they don't immediately manipulate the DOM), then you might be able to remove your code from the$(document).ready…function, and have them work as expected.Inline scripts are generally discouraged because traditionally they have indicated that the code will only run on the page that it's included on, and that any event listeners will be automatically destroyed when the user navigates away. However, in Turbolinks apps this isn't the case. If you add an event listener in an inline script (and don't remove it), it will continue to listen and run on subsequent page loads. As you found by setting up a
turbolinks:loadevent listener in this way, the handler triggered on subsequent page loads, and on revisiting the first page, the event listener was added again, resulting in duplicate calls.Regarding setting up "per-page JavaScript state" and "bootstrap client-side models", let's say you have a calendar page that you wish to enhance with some JavaScript. You might have a
CalendarJavaScript class which handles this, so you could include the following to bootstrap it with some JSON:(bearing in mind that any added event listeners set up within the
Calenderclass should probably be destroyed at some point e.g.turbolinks:before-cache).That demonstrates the idea, but a better approach in this case might be to set up a system that looks for calendar elements and in the DOM then instantiates them. For example:
then in your JavaScript (included in an application bundle):
This approach can be generalised and I've walked through one implementation here: https://stackoverflow.com/a/44057187/783009
Hope that helps!