If you have a look at the default application.js
file generated by Rails,
you’ll see //= require jquery_ujs
. You might know exactly what this require is
for, but if you’re like me you know you need it but have only a vague idea of
what it’s responsible for.
Maybe you’ve looked at that line and thought, “I should really figure out what that does someday.” Well, for me, today is that day. I thought you might want to join me.
Unobtrusive JavaScript
The UJS in jquery-ujs
stands for unobtrusive JavaScript. This is a
rather broad term that generally refers to using JavaScript to progressively
enhance the user experience for capable browsers without negatively impacting
clients that do not support or do not enable JavaScript.
jquery-ujs
wires event handlers to eligible DOM elements to provide enhanced
functionality. In most cases, the eligible DOM elements are identified by HTML5
data attributes.
Let’s have a look at the progressive enhancements jquery-ujs
provides.
POST, PUT, DELETE Links
<%= link_to 'Delete', item, method: :delete %>
Clicking a link will always result in an HTTP GET
request. If your link
represents an action on a resource it may be more semantically correct for it
to be performed with a different HTTP verb; in the case above, we want to use
DELETE
.
jquery-ujs
attaches a handler to links with the data-method
attribute. When the link is clicked, the handler constructs an HTML form along with a hidden input that sets the _method
parameter to the requested HTTP verb and submits the form rather than following the link.
Confirmation Dialogs
<%= form_for item, data: { confirm: 'Are you sure?' } %>
jquery-ujs
attaches a handler to links or forms with the data-confirm
attribute that displays a JavaScript confirmation dialog. The user can choose
to proceed with or cancel the action.
Disabling Links and Buttons
<%= form.submit data: { disable_with: 'Submitting...' } %>
Users double click links and buttons all the time. This causes duplicate
requests but is easily avoided thanks to jquery-ujs
. Links and buttons that
have a data-disable-with
attribute get a click handler that disables the
element and updates the text of the button to that which was provided in the
data attribute and disables the button. If the action is performed via AJAX,
the handler will re-enable the button and reset the text when the request
completes.
AJAX Forms
<%= form_for item, remote: true %>
Adding remote: true
to your form_for
calls in Rails causes jquery-ujs
to
handle the form submission as an AJAX request. Your controller can handle the
AJAX request and return JavaScript to be executed in the response. Thanks to
jquery-ujs
and Rails’ respond_with
, setting remote: true
is likely the
quickest way to get your Rails application making AJAX requests. The unobtrusive
nature of the implementation makes it simple to support both AJAX and standard
requests at the same time.
AJAX File Uploads
Browsers do not natively support AJAX file uploads. If you have an AJAX form
that contains a populated file input, jquery-ujs
will fire the
ajax:aborted:file
event. If this event is not stopped by an event handler, the
AJAX submission will be aborted and the form will submit as a normal form.
Remoteipart is one Rails gem that hooks into this event to enable AJAX file uploads.
Required Field Validation
HTML5 added the ability to mark an input as required. Browsers with
full support for this feature will stop form submission and add browser-specific
styling to the inputs that are required but not yet provided. jquery-ujs
adds
a polyfill that brings this behavior, minus the styling, to all
JavaScript-enabled browsers. There’s no default styling provided by the
polyfill, which means users of impacted browsers may be puzzled as to why the
form will not submit. There’s an ongoing discussion about the
appropriateness of this polyfill.
You can opt-out of this behavior by setting the “novalidate” attribute on your
form. This will cause both the jquery-ujs
polyfill and browsers with native
support to skip HTML5 input validation. Given both the potential for confusion
in browsers without native support and the fact that browsers with native
support apply styles that may clash with your site design, handling validation
is probably better left up to each developer.
Cross-Site Request Forgery Protection
Cross-Site Request Forgery (CSRF) is an attack wherein the attacker tricks the user into submitting a request to an application the user is likely already authenticated to. The user may think he’s simply signing up for an email newsletter, but the attacker controlling that sign up form is actually turning that into a request to post a status to some other website, using the users preexisting session.
Rails has built in protection which requires a token only available on
your actual site to accompany every POST
, PUT
, or DELETE
request. In
collaboration with <%= csrf_meta_tags %>
in your application layout HEAD
,
jquery-ujs
augments this protection by adding the CSRF token to a header on
outgoing AJAX requests.
jquery-ujs
also updates the CSRF token on all non-AJAX forms on page load,
which may be out-of-date if the form was rendered from a fragment cache.
Extensibility
jquery-ujs
exposes its functions in the $.rails
namespace and fires many
events when submitting AJAX forms. jquery-ujs
behavior can be
customized by overriding these methods or handling the appropriate events. We’ve
already seen Remoteipart
as an example of custom event handling. There also
exist several gems that override $.rails.allowAction
to replace JavaScript
confirmation dialogs with application-specific modal dialogs.