Rails jQuery UJS: Now Interactive

The Rails jQuery UJS adapter (aka jquery-ujs) has undergone some major renovations over the past few weeks. This particular change is one I’m very excited about, as I’ve been wanting to do this for a while. Without further ado, jquery-ujs is now InteractiveTM.
What do I mean by “interactive”? Well, we can now plug into every facet of jquery-ujs, binding to custom events, and even customizing internal functions, without hacking or monkey-patching the rails.js file itself.
We’ll start with a brief explanation of the change and then dive into a few examples.
Feel free to bypass my blabbering and check out the the commit.
Closure style
Previously, rails.js was one big closure.
Before:
...
function fire(obj, name, data) {
var event = $.Event(name);
obj.trigger(event, data);
return event.result !== false;
}
...
})( jQuery );
// cannot access fire() function here
This closure design had many advantages. However, the biggest advantage, in this case, happened to be the biggest disadvantage as well: everything inside was self-contained and inaccessible by the outside world.
So, what if we like most of what jquery-ujs does for us, but just want to modify one little function? Before, we’d have to open rails.js in our editor and start hacking away. But no more!
Object literal style
A relatively simple refactor using the object literal design pattern now allows us total control, by placing all internal functions into the externally-accessible $.rails object.
After:
var rails;
$.rails = rails = {
...
fire: function(obj, name, data) {
var event = $.Event(name);
obj.trigger(event, data);
return event.result !== false;
}
...
};
...
})( jQuery );
// Can now access $.rails.fire() here
Now, inside rails.js, we simply change any fire() function calls to rails.fire(). And outside of rails.js, we can similarly call $.rails.fire().
Furthermore, we could even redefine the fire() function with our own: $.rails.fire = someNewFunction();.
In the excerpted code from rails.js above, the “before” looks simpler, but the “after” actually does a nice job of keeping things clearer and better organized when there are more functions.
Some examples
So, how might we actually use this now in a production app? Let’s look at a couple examples.
Direct access
For starters, let’s say that we want for a particular form to be submitted the same as :remote => true forms, but for whatever reason, this form does not have the remote attribute. We can now take advantage of the functionality provided by rails.js directly. In our application.js, we might have:
$.rails.handleRemote( $(this) );
e.preventDefault();
});
And now, rails.js will set up our AJAX request, serialize our form, etc., just as if it were a remote form. Of course, this example is a bit contrived, but hopefully it illustrates the potential for being able to access rails.js functions directly.
Redefining jquery-ujs functions
And of course, as mentioned before, there’s also the ability to redefine functions. For example, in our application.js, we could add:
var event = $.Event(name);
// Custom code:
// e.g. "Fire ajax:before for my-form!"
console.log("Fire " + name + " for " + obj.attr('id') + "!");
obj.trigger(event, data);
return event.result !== false;
};
And now we have our own custom fire() function which will now be used internally by jquery-ujs without having to open up and modify rails.js.
On a side-note, the fire() function in rails.js is probably one of my favorite little functions that I’ve actually used in several other projects, including the jQuery EasyTabs plugin. But we’ll take a closer look at this function another time.
Redefining selectors
And as an added bonus, we also moved the CSS selectors, to which jquery-ujs binds all its functionality, into the $.rails object. How could this be useful?
Let’s say we want the ability to kill all jquery-ujs form bindings for a particular form (maybe after it’s been submitted once already). In rails.js we have:
...
// Form elements bound by jquery-ujs
formSubmitSelector: 'form',
...
}
Now in our application.js, we could do something similar to the following:
$('#my-form').live('ajax:success', function() {
$(this).attr('data-skip-remote', 'true');
});
W00t
Well I hope this was helpful. One of my goals with these changes was to spur the development of additional plugins and gems that build off of and extend the great out-of-the-box functionality that rails.js provides.
As another way to help maintain third-party plugins that extend jquery-ujs, I also added a new Changelog. This will hopefully make it easier to keep track of notable changes to the “api”.
I’d also like to send a big thank-you to Neeraj Singh, who has been very helpful keeping me sane and in getting changes pulled into jquery-ujs.
Stay tuned for more on recent developments in Rails jQuery UJS. In the meantime, I encourage you to check out the source.
If you liked this, check out our Definitive Guide to Rails 3 Remote Links and Forms Part 1 and Part 2.

That’s really a great news, I see a lot of posibilities for improving a lot of my code because of this.
This seems to address the issue regarding including other elements that could make an HTTP req that you helped me with before right?
Yussir, this would do it
Though it might be nice down the road to have full support built in by default.
You might want to remove the link from the article, that submits this page to HN. That appeared when I first came here from HN.
Hi Vijay, are you referring to the link at the bottom? That allows you to conveniently share/upvote the article on Hacker News when you’re done reading if you so choose
Thanks a lot Steve,
I found a very useful scenario for your “Direct Access” example. I have a form for submitting an application, but before submission I need to make sure that a few photos are uploaded. Here’s the code:
event.preventDefault();
var that = $(this);
$.when(upload(img1), upload(img2)).done(function() {
$.rails.handleRemote( that );
});
});
Hey Gur, glad you were able to use this.
In case anyone else is wondering, normally you could attach events that need to be executed before a remote form is submitted to the
ajax:beforeevent hook that jquery-ujs fires. However, in this case, calling the deferred object with$.when()would return true before the uploading was finished.So, instead, you can just leave off the
:remote => trueattribute and manually call the internal jquery-ujs$.rails.handleRemote()function manually when you’re ready for it.Just goes to show how flexible the object-literal pattern is.
You nailed it, that was the exact problem I was having with the “ajax:before” hook. Thanks again.
Yahoo….exactly what I was looking for. Much Thanks.