How Turbolinks Affect jQuery in Ruby on Rails

Motivation

I recently worked on the navigation menu in a Ruby on Rails web application. The menu relied on the toggleClass function from jQueryUI. When a click event would take place on the menu, the “active” class would be toggled. Toggling the active class would either shift the menu 200 pixels to the left to reveal the content of the navigation menu, or shift the menu 200 pixels to the right to hide the content depending on whether the class was added or removed, respectively.

Everything worked well. Then I found a bug.

I found that if I clicked on a link that took me to another view within the Rails app and then somehow navigated back to the root page (the root page contained said navigation menu), then the menu would no longer respond to clicks. If I refreshed the page, however, then the menu would work properly again. Strange.

For a while I had no idea what was causing the problem. I (wrongly) suspected that perhaps the culprit was the very last action that I had taken, which was to click on a link from a different view. Was my browser’s focus somehow trapped inside the view I had just navigated away from? I searched online for others who had this problem and didn’t find anything helpful. As I later learned, I was searching all wrong.

Thanks to the help of a friend, I was eventually able to understand the cause of the problem: Turbolinks.

Walkthrough

The jQuery code that I wrote in my script.js file (saved in MyAppName/app/assets/javascripts) to add responsiveness to my navigation menu looked something like this:

var main=function() {
$(".menu-button").click(function(e) {
$("div.menu").toggleClass("active", 200);
e.preventDefault();
});
};
$(document).ready(main);

This is where Turbolinks matter. The Turbolinks gem is included by default in the Ruby on Rails framework. You can see it in the application.js file in MyAppName/app/assets/javascripts (via the //=require turbolinks line). The Turbolinks gem makes web applications fast because it allows views to be changed without a full-page reload. Ever notice how web pages sometimes fully reload when you navigate to another page within the same site? That doesn’t happen when using Turbolinks, even though the content you see after navigating to a new page (i.e. to a new ‘view’) can be 100% different.

Turbolinks are great. BUT….

Turbolinks affect JavaScript and jQuery that manipulate the DOM.

$(document).ready(main) tells browsers to run JavaScript code when a page is downloaded or refreshed. When navigating to a page via Turbolinks, a download or (full-page) refresh doesn’t happen. This means that the browser doesn’t know that it should run the main function. You can find a better explanation of this here.

Luckily, the solution lies in one short line added to the bottom of the script.js file:

var main=function() {
$(".menu-button").click(function(e) {
$("div.menu").toggleClass("active", 200);
e.preventDefault();
});
};
$(document).ready(main);
$(document).on(‘page:load’, main);

page:load is a Turbolinks event that fires at the end of the shortened Turbolinks loading process. Listening for both $(document).ready and $(document).on(‘page:load’) will ensure that JavaScript code will continue to run as you navigate across your web application.

 

Did you find this walkthrough helpful?

Do you have questions on the above?

Do you see any errors?

 

If so, please leave your thoughts in the comments below.