Turbolinks Caching with JavaScript Modified Dom
Avoid duplicated markup by preparing your document for caching
Code Web Design by Simplu27 is licensed under CC0
Turbolinks takes advantage of a lot of benefits of a single page application without needing to actually build or add the complexity of a client-side JavaScript framework. One of the ways it achieves the snappy page loads of a single page application in a traditional, link-based application is caching. When a page is visited, Turbolinks will cache the final state of the visited page. That includes caching the final markup of the document, the stylesheets, image assets, and JavaScript.
Caching Creates Duplicate Markup
With many plugins, a traditional web application will initialize JavaScript plugins on page load and those plugins will often create and maintain its own markup, and event listeners, and handlers. In many cases, this works great, even in a Turbolinks-enabled application. Sometimes, though, the caching can create duplicate markup.
Example
The application I am working on has a form that utilizes Chosen to replace some of the select boxes. Chosen is initialized on page load, looks for select tags that are tagged as Chosen selects, and then applies itself on the dom by creating new markup while setting up listeners and handlers for those selectors.
In Turbolinks, the resulting cached version of a page, post-Chosen, will include all of the new markup created, along with assets. When a user navigates normally through the app, Turbolinks displays the cached version as a preview while fetching a new version of the page. Turbolinks then replaces the cached body with the new body while merging the headers. In restoration visits, when a user navigates using the forward and back browser buttons, Turbolinks only displays the cached version but will rerun the (cached) styles and JavaScript initializers.
The result is that when the cached version of the markup is rendered, it will include the original Chosen markup. However, that markup is now orphaned since the JavaScript listeners and handlers are no longer linked. When the JavaScript is rerun, Chosen believes it needs to initialize again and therefore creates duplicate markup.
The fix is pretty simple in this case. Per the documentation, you can prepare the document prior to Turbolinks caching it.
document.addEventListener("turbolinks:before-cache", function() { // clean up dom });
You can do all sorts of things in here. In our case, we wanted to stop Chosen from orphaning markup. To do this we simply destroy all instances of Chosen prior to cache, and when it loads, let it reinitialize.
document.addEventListener("turbolinks:before-cache", function() { $("[data-chosen]").chosen("destroy"); });
That’s it!
Caveats
In most cases, this solution will work and it is as easy as replacing your typical ready
or onload
function with the turbolinks:load
document.addEventListener("turbolinks:load", function() { // do stuff here });
The caveat is that any setup you do put in here, or if you keep it in your traditional ready
function, it needs to be idempotent, or in other words, safe to run multiple times without changing the result beyond its initial application. You can read the Turbolinks documentation on ensuring your setup is idempotent.
In our example, this wouldn’t entirely work because the event listeners and handlers were no longer linked to the markup and Chosen does not offer a way to relink orphaned markup. There would only be a single set of markup for Chosen, but it would not be functional.
Comments
Thank you laura !! I tried for 1 week to find the solution.
Must be glad for the given this nice article here this is the very nice to play here free online hearts game forever.