Quickstart Guide

Installation

You can install the UJS plugin from our Subversion repository with Rails’ plugin script:

ruby script/plugin install http://source.ujs4rails.com/current/unobtrusive_javascript

If everything goes to plan the installation process should copy lowpro.js into the public/javascripts directory. Have a quick look to confirm that it’s there – if for some reason it isn’t simply run the following command:

rake unobtrusive_javascript:install

Once you’ve confirmed that lowpro.js is installed, the final installation step is to open your routes.rb file and add the following line into the Routes.draw block:

ActionController::Routes.draw do |map|
    UJS::routes
    # your routes here.
end

It shouldn’t matter where in your Routes.draw block you insert the Unobtrusive Javascript routes – if you have any problems, try changing the order of your routes file a bug report if you still have problems.

Note: UJS (and the lowpro extensions) requires at least Prototype 1.5.0 RC0.

The UJS routes makes sure that all requests to ’/behaviour/any/sub/path’ to it’s own built-in controller so it should be easy to avoid collisions. Once you’ve installed the routes, you’re ready to rock.

Including The Scripts

The main purpose of UJS is to help you remove the JavaScript that normally litters your HTML pages and put it all away in a neat external JavaScript file that can be cached by the browser. In order for this to work you need to reference this javascript file as well as lowpro.js, the library that contains a lot of the magic behind UJS.

Simply use javascript_include_tag to add the UJS scripts into the document’s head tag as shown:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
          <%= javascript_include_tag 'prototype', :unobtrusive %>

As you might expect you need to at least include prototype as well. If you intend to use Scriptaculous scripts, be sure to include those as well. It’s normally best to avoid using :defaults to save having the user download over a hundred KB of unused JavaScript. You can safely use :defaults and :unobtrusive together in the same javascript_include_tag call.

Applying Behaviours

Unobtrusive JavaScript is all about creating a working application and then layering on top your JavaScript ‘behaviour’ to create a richer interface. By working this way, it’s possible to create applications that are usable even with JavaScript disabled.

Instead of taking the normal Rails approach of tangling up your behaviour in your HTML, UJS favours a more declarative approach which centers around the apply_behaviour and apply_behaviours helper methods (don’t worry Americans, UJS knows about the American spellings too, so apply_behavior will work as well).

Let’s start with a really simple example:

<% apply_behaviour 'a.external:click', 'window.open(this.href, "ext"); return false;' %>

As a result of adding this to your view template, once the DOM has fully loaded (but not images and other assets) all links with a class attribute of “external” will execute the given behaviour when they are clicked (you may be familiar with the ‘selector:event’ syntax from the event:Selectors library). Notice that in the snippet of JavaScript above, this refers to the element the behaviour is being applied to.

As a result of the above behaviour declaration, all links with the class ‘external’ will open in a new window. It’s functionally equivalent to having a page full of links like this:

<a href="" 
   onclick="window.open(this.href, 'ext'); return false;">UJS For Rails</a>

You’ll also notice that we added “return false” to the end of our JavaScript to stop the original click event (following the link) from taking place. You could also do this if you are using one of the Rails built-in JavaScript helpers:


<% apply_behaviour 'a.widget', make_remote_link << "; return false" %>

However, thats not exactly elegant; instead, you can just do this:


<% apply_behaviour 'a.widget', make_remote_link, :prevent_default => true %>

If you look at the source of the page you created with apply_behaviour you’ll find it’s beautifully free of JavaScript. It’s all hidden away in a single line of a cached JavaScript file. Good eh?

Writing large behaviours as strings of JavaScript can get tedious very quickly – what if you could write your behaviours using pure Ruby, RJS-style? Well that’s not a problem either. apply_behaviour takes an RJS formatted block too:

<% apply_behaviour 'a.external:click' do |page, element, event|
      page.alert "A-B-C ya, and I wouldn't wanna be ya." 
      element.visual_effect :slide_down, :duration => 2
      event.stop  
end %>

Calling event.stop is another way of stopping the original event. You can still use :prevent_default => true instead however.

Finally, there are some times you want to apply a whole group of behaviours at once. apply_behaviours is there to stop you getting RSI:

<% apply_behaviours {
    on '#myform:submit', 'return validate(this)'
    on 'a.widget:click', { |page| page.call 'Widget.activate' }
} %>

If you want to get DRY, you can put these blocks into helper methods to create whole chunks of reusable behaviour.

Using The Rails JavaScript Helpers

As noted previously, apply_behaviour is the best way to add behaviours to your view but we realise that a lot of Rails developers are used to the built-in Rails JavaScript Helpers – so we’ve fixed them too! In fact, with very little work it should be pretty easy to retrofit UJS to existing apps to get instant UJS goodness.

If you want to, you can use all of the old favourites like link_to_remote and remote_form but still have your JavaScript stashed away and cached in an external JavaScript file. We’ve even retrofitted the tag helpers to be unobtrusive too:

<%= content_tag 'div', 'click me for web 2.0 glory!', 
                :onclick => '$(this).visualEffect("highlight")' %>

This works as normal but if you check the source you’ll see no sign of the JavaScript, thanks to a bit of UJS magic. Of course, if you need the script written into the tag for some reason (or you are just feeling evil), you can add the :external => false option to revert to old fashioned ‘Waiter, there’s behaviour in my content!’ mode.

Caching Your Behaviours

Whether you are an unobtrusive scripting convert or not, the benefits of having your behaviour JavaScript held in an external file are undeniable; the browser can cache the script once, saving bandwidth on subsequent refreshes of a page. UJS achieves this by using the If-None-Matched and Etag HTTP headers to decide whether or not the application needs to send you a new version of the JavaScript behaviours file or not. This first level of caching comes for free – UJS figures out when to expire the behaviours without any intervention from you.

For pages that have constant behaviour, UJS employs caches_behaviour to cache behaviours to a static file in the same way as Rails’ page caching mechanism. This way requests don’t hit the application at all. It works exactly like page caching – in your controller just specify which actions you want to cache the behaviour of:

class NinjaController < ApplicationController
     caches_behaviour :index, :smoke_bombs
     # actions here.
 end

However, now you need to expire the behaviours explicitly. Unsurprsingly, you use expire_behaviour:

def new_thing
     # do stuff
     expire_behaviour :action => 'index'
end

Behaviour caching is also integrated with page and action caching. If you cache a page or action it caches the associated behaviour. If you expire them it expires the behaviour too. You don’t need to worry about it.

All feedback and suggestions for improvements to this Quickstart Guide are welcomed. Have fun!

Return to home page