I have been reviewing the most common issues with Turbolinks with the aim of providing some ideas as to what we might include in the Rails guides. Obviously Turbolinks is a separate project to Rails, so I'm still questioning what belongs where. However I thought it might serve as a starting point for some ideas.
Feedback on whether this fits in with the scope of this project is welcome, or if anyone has ideas for sections or topics, then let me know.
Many third-party libraries rely on DOMContentLoaded to initialise, and therefore won't re-initialise on subsequent Turbolinks page loads. Bootstrap's data-* API exemplifies this; developers will discover that their behaviours are added on the initial load and on refresh, but not thereafter. Rather than using the automatic data-* methods, it is preferable to manually control third-party libraries by calling their JavaScript functions directly if possible. (This a form of "conceptual expansion": developers can no longer just add data-* attributes and expect it to work; coming from this path, they'll have to have some concept of how Turbolinks works. I suppose this is one tradeoff for snappier page loads.)
Unfortunately some libraries (such as chat widgets) can only be integrated by adding a script tag to the <body>. This can cause errors when the widget's HTML is destroyed in a Turbolinks render, but the library's internal state is not. Currently there is no good solution to this, but perhaps there will be in the upcoming release.
On the plus-side, pulling in a load of jQuery plugins is not as prevalent as it used to be.
Running page-specific JavaScript
It's common to customise behaviour for a specific page by adding a <script> at the bottom of a page, then running code within a turbolinks:load listener. (This stems from the common practice of sprinkling $(document).ready(…) snippets in the <body>.) With Turbolinks, this can cause some issues. Firstly, it gives the impression that the code will run only on that page, but code in a turbolinks:load listener will run on every subsequent page load. What's more, if a user revisits that page, the listener will be attached again and again, resulting in confusing duplication. Secondly, turbolinks:load could be fired before the code has loaded, meaning it will never run. Often the solution is to run the code outside the turbolinks:load listener, but in general, inline scripts should be avoided.
There are often questions about organising code for running on a specific page. Stimulus encourages developers away from the "page-specific" mindset, and towards organising code in controllers that are automatically initialised whenever the related element is rendered; be that on a Turbolinks page load or via some other dynamic rendering. Developers may not be in a position to reorganise their JavaScript as Stimulus controllers, but perhaps it's worth steering them in that direction.
Understanding Caching & Previews
The Turbolinks caching mechanisms and previews are great, but if you're not aware of them as features, their behaviours might seem unexpected. Developers often ask why they briefly see the old page version, or why their plugin has reinitialised. Previews are mentioned in the Application Visits section of the Turbolinks readme, and there is a really great section on Understanding Caching but perhaps this could be promoted more as a feature and outlined in the Rails guides.
Forms
Turbolinks doesn't currently handle form submissions. turbolinks-rails does support redirects in collaboration with rails-ujs (typically after a successful form submission), but submitting and directly rendering a form response as HTML is not supported by Turbolinks itself. From what I gather, form submissions will be supported in the next version 🎉
I'm going to wait and see on this until the new Turbolinks comes out, which I think will change some of this, and some of this documentation my be produced separately.
I have been reviewing the most common issues with Turbolinks with the aim of providing some ideas as to what we might include in the Rails guides. Obviously Turbolinks is a separate project to Rails, so I'm still questioning what belongs where. However I thought it might serve as a starting point for some ideas.
Feedback on whether this fits in with the scope of this project is welcome, or if anyone has ideas for sections or topics, then let me know.
Integrating with third-party libraries
Many third-party libraries rely on
DOMContentLoaded
to initialise, and therefore won't re-initialise on subsequent Turbolinks page loads. Bootstrap'sdata-*
API exemplifies this; developers will discover that their behaviours are added on the initial load and on refresh, but not thereafter. Rather than using the automaticdata-*
methods, it is preferable to manually control third-party libraries by calling their JavaScript functions directly if possible. (This a form of "conceptual expansion": developers can no longer just adddata-*
attributes and expect it to work; coming from this path, they'll have to have some concept of how Turbolinks works. I suppose this is one tradeoff for snappier page loads.)Unfortunately some libraries (such as chat widgets) can only be integrated by adding a script tag to the
<body>
. This can cause errors when the widget's HTML is destroyed in a Turbolinks render, but the library's internal state is not. Currently there is no good solution to this, but perhaps there will be in the upcoming release.On the plus-side, pulling in a load of jQuery plugins is not as prevalent as it used to be.
Running page-specific JavaScript
It's common to customise behaviour for a specific page by adding a
<script>
at the bottom of a page, then running code within aturbolinks:load
listener. (This stems from the common practice of sprinkling$(document).ready(…)
snippets in the<body>
.) With Turbolinks, this can cause some issues. Firstly, it gives the impression that the code will run only on that page, but code in aturbolinks:load
listener will run on every subsequent page load. What's more, if a user revisits that page, the listener will be attached again and again, resulting in confusing duplication. Secondly,turbolinks:load
could be fired before the code has loaded, meaning it will never run. Often the solution is to run the code outside theturbolinks:load
listener, but in general, inline scripts should be avoided.There are often questions about organising code for running on a specific page. Stimulus encourages developers away from the "page-specific" mindset, and towards organising code in controllers that are automatically initialised whenever the related element is rendered; be that on a Turbolinks page load or via some other dynamic rendering. Developers may not be in a position to reorganise their JavaScript as Stimulus controllers, but perhaps it's worth steering them in that direction.
Understanding Caching & Previews
The Turbolinks caching mechanisms and previews are great, but if you're not aware of them as features, their behaviours might seem unexpected. Developers often ask why they briefly see the old page version, or why their plugin has reinitialised. Previews are mentioned in the Application Visits section of the Turbolinks readme, and there is a really great section on Understanding Caching but perhaps this could be promoted more as a feature and outlined in the Rails guides.
Forms
Turbolinks doesn't currently handle form submissions. turbolinks-rails does support redirects in collaboration with rails-ujs (typically after a successful form submission), but submitting and directly rendering a form response as HTML is not supported by Turbolinks itself. From what I gather, form submissions will be supported in the next version 🎉