I ran into an issue in my Rails 4 app while trying to organize JS files "the rails way". They were previously scattered across different views. I organized them into separate files and compile them with the assets pipeline. However, I just learned that jQuery's "ready" event doesn't fire on subsequent clicks when turbo-linking is turned on. The first time you load a page it works. But when you click a link, anything inside the ready( function($) {
won't get executed (because the page doesn't actually load again). Good explanation: here.
So my question is: What is the right way to ensure that jQuery events work properly while turbo-links are on? Do you wrap the scripts in a Rails-specific listener? Or maybe rails has some magic that makes it unnecessary? The docs are a bit vague on how this should work, especially with respect to loading multiple files via the manifest(s) like application.js.
Found this in the Rails 4 documentation, similar to DemoZluk's solution but slightly shorter:
OR
If you have external scripts that call
$(document).ready()
or if you can't be bothered rewriting all your existing JavaScript, then this gem allows you to keep using$(document).ready()
with TurboLinks: https://github.com/kossnocorp/jquery.turbolinksHere's what I have done to ensure things aren't executed twice:
I find using the
jquery-turbolinks
gem or combining$(document).ready
and$(document).on("page:load")
or using$(document).on("page:change")
by itself behaves unexpectedly--especially if you're in development.None of the above works for me, I solved this by not using jQuery's $(document).ready, but use addEventListener instead.
I just learned of another option for solving this problem. If you load the
jquery-turbolinks
gem it will bind the Rails Turbolinks events to thedocument.ready
events so you can write your jQuery in the usual way. You just addjquery.turbolinks
right afterjquery
in the js manifest file (by default:application.js
).NOTE: See @SDP's answer for a clean, built-in solution
I fixed it as follows:
make sure you include application.js before the other app dependent js files get included by changing the include order as follows:
Define a global function that handles the binding in
application.js
Now you can bind stuff like: