$(document).ready function in combination with CSS style scoping to limit the functionality.
- Behavior that’s “always on”
- Behavior that’s triggered from a user action
But first, a few things to will help us stay organized…
I still like to scope the body element of the layout(s) with the controller and action name:
This not only let’s us control access to the DOM through jQuery if we need to, but also provides some top-level styling classes to allow us to easily add page-specific CSS.
In the case we’re working on the proverbial blog posts application, the body tag ends up looking like:
posts-related pages in the controller with the
.posts class, or down to the specific page using a combination of both the controller and action:
Default Application Manifest
Here’s the default
I start by removing the line
So we’re left with:
Let’s start by adding the file
Let’s dig in to each pagef of this:
We’re creating the
App object on window so the functionality added to the object is available throughout the application.
Next, we define an
init() function on
The call to
$("a, span, i, div").tooltip() initializes Bootstrap Tooltips. This is an example of the type of libraries that can/should be setup here. Obviously, if you’re not using Bootstrap tooltips, you would haven’t this here, but coupled with the next line, we’ll see why this works.
As many have found out the hard way, when Turbolinks is enabled in a project, jQuery
$(document).ready functions don’t get fired from page to page. In order to call the
init() function on each page transition, we’ll hook in to the
turbolinks:load transition is also triggered on the well known document ready event, so there’s no need to add any special handling for first page load.
Lastly, we need to add
init.coffee to the asset pipeline:
Now with the defaults out of the way, let’s take a look at adding some behavior.
A few things to note here…
I created a class in the
App namespace — the same we initialized in
You might notice the file takes the form:
While this may seem obvious, it’s an important point to keep in mind. I’ve found it offers a predictable structure that allows me to open any coffeescript file that we’ve written in the project and generally know where to look for what.
We called this “Always On” functionality because, as you probably noticed, using the following event listener
$(document).on "turbolinks:load", ->, we know with Turbolinks, this gets triggered on every page transition.
Add to Manifest
Because we removed the
//= require_tree . line in the default
application.js manifest file, we’ll have to add our chart file to be included in the asset pipeline (last line):
Uh oh, so maybe we don’t want the graph to show up on every page! In this case, we’re looking for “Always On” functionality for specific pages ONLY.
We can limit the page pages certain functionality is loaded on by using the classes we added to the body of the layout. In this case, a small conditional to the invocation can prevent this being triggered on pages it shouldn’t be.
return unless $(".posts.index").length > 0 to make sure
App.Chart never gets instantiated if we’re on the
Let’s assume there’s a link in the user’s account that allows them to update their credit card. In this case, we have the following:
You’ll probably notice the
We could have added a unique class to the link:
or, perhaps, even assign an ID:
Both of these techniques don’t really indicate whether we added the
We can use the selector
[data-behavior~=update-credit-card] to latch on to the
data-behavior tag we defined in the view. We use the
We could latch on to the
change event, or whatever is appropriate to the element we’re adding behavior.
Add to Manifest
//= require app.billing to the manifest file:
One thing that makes me feel good about this approach is there’s no real magic or extra plugins. It’s using all the tools we already have in a basic Rails application, which is one less thing to maintain and keep up to date. Less depedencies == less pain down the road.