<?xml version="1.0" encoding="UTF-8"?>
<page>
  <body>h3. Protect yourself from the browser

Although JavaScript in itself is a nice little language, the state of the implementations in various browsers is a minefield, to say the least. You probably don't want to have to deal with that, and luckily there are many who've solved these problems already and packaged them into nice little (or large) libraries such as &quot;Prototype&quot;:http://www.prototypejs.org/, &quot;jQuery&quot;:http://www.jquery.com and &quot;YUI&quot;:http://developer.yahoo.com/yui/. Pick one, pick two, learn how to use them and their individual strengths and weaknesses. There are lots of articles out there that teach you how to use these libraries, and I will be using Prototype in the examples as it's probably the one most Rails developers know the best. There are probably some mistakes and bad practises (such as using @innerHTML@) in my Prototype code, and I haven't tested it, but as long as you can understand what I'm _trying_ to do that's ok.

h3. Everything in its right place

First off, make sure you make good use of namespacing. An HTML page can have many unrelated JavaScript files included in it, and if they're not careful about namespacing they could interfere with each other. That's the reason you _have to_ use namespaces. Namespaces also provide you with some structure in your script. That's a reason you _should_ use them. When you first start out with a site, it may not seem like such a big deal, and just chucking everything into @application.js@ with a few (global) functions to DRY things up seems like the optimal solution. But when you start adding stuff to the site and that new stuff also needs some accompanying JavaScript, this quickly becomes messy and difficult to maintain.

This is the approach I've settled on lately, and it seems to be working pretty well:

* In the global scope, I create a (hopefully) unique variable. This is where I put _all_ of my stuff. I'm not as religious about this one as I probably should be, sometimes I define the application variables globally. This object can also have functions, classes and objects I want to reuse across different sites.
* I have an object for each of my apps, usually defined as a property on the global object mentioned above.
* On the application object I define everything that belongs to that application, at various levels.

So, let's say we have a company named _Skidooxoo_ which creates a &quot;Blog 2.0&quot; (because we think it's so much better than existing blog software that it should be called _next generation_ blogging software) software with Rails named &quot;Scriblr&quot;. This blog application will of course be filled with all sorts of unobtrusive Ajax and other JavaScript. For this application, the namespaces would look like this:

[javascript]
//The global object. All of Skidooxoo's stuff goes in here
var Skidooxoo = {};

//The application object. Everything related to this app goes in here
Skidooxoo.Scriblr = {
  version: 'forever beta'
};
[/javascript]

Now we've created a _place for things_. As we start developing the application, we'll put everything into this namespace.

h3. Encapsulate common elements

As we add stuff to the application, more and more elements will appear in the HTML which the JavaScript needs to know about. We will find these elements by searching the DOM for their class names or tag names; most of the JS libraries let you use CSS or XPATH expressions to locate elements. But instead of just gathering them up and sending them off to work, we'll add some more structure to the process by encapsulating these elements with _classes_. The goal is to have each element encapsulated in a JacaScript object that knows its own state and knows how to perform certain actions. To demonstrate, let's say we're adding blog posts with corresponding comments to our app. A blog post can appear alone or along with others and may or may not have its comments displayed. We'll distinguish these elements by their class names, &quot;post&quot; and &quot;comment&quot;. In addition, each element will have an ID telling us which ID it has on the server, such as &quot;post_1&quot; or &quot;comment_4&quot;. This is good to know when adding Ajax which uses these IDs when communicating with the server.

A page showing a single blog post with comments could have HTML looking something like this:

[html]
&lt;div class=&quot;post&quot; id=&quot;post_1&quot;&gt;

  &lt;h2 class=&quot;title&quot;&gt;My first posting&lt;/h2&gt;

  &lt;p&gt;
    Hello and welcome to my new blog. It's going to be awesome!
  &lt;/p&gt;

  &lt;h3&gt;Rate this post&lt;/h3&gt;

  &lt;ul class=&quot;rating&quot;&gt;
    &lt;li&gt;&lt;a href=&quot;/posts/1/rate/1&quot;&gt;1&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/posts/1/rate/2&quot;&gt;2&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/posts/1/rate/3&quot;&gt;3&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/posts/1/rate/4&quot;&gt;4&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;/posts/1/rate/5&quot;&gt;5&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;

  &lt;h3&gt;Comments&lt;/h3&gt;

  &lt;div class=&quot;comments&quot;&gt;

    &lt;div class=&quot;comment&quot; id=&quot;comment_1&quot;&gt;
      &lt;h3 class=&quot;name&quot;&gt;Randy Bobandy&lt;/h3&gt;
      &lt;p&gt;
        Welcome to the internet!
      &lt;/p&gt;
    &lt;/div&gt;

    &lt;div class=&quot;comment&quot; id=&quot;comment_2&quot;&gt;
      &lt;h3 class=&quot;name&quot;&gt;Mr. Lahey&lt;/h3&gt;
      &lt;p&gt;
        Oh no, not another blog :/
      &lt;/p&gt;
    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;
[/html]

Here we have two types of elements that need to be extracted and encapsulated with JavaScript, _posts_ and _comments_. Let's start with creating a class for posts, with some class and instance methods and properties.

[javascript]
(function(Scriblr){

Scriblr.Post = function(element){
  this.element = element;
};

//&quot;Class&quot; methods
Object.extend(Scriblr.Post, {
  findPosts: function(){
    return $$('.posts').map(function(div){
      return new Scriblr.Post(div);
    });
  }
});

//&quot;Instance&quot; methods
Object.extend(Scriblr.Post.prototype, {
  getTitle: function(){
    return this.element.down('.title').innerHTML;
  },
  setTitle: function(title){
    this.element.down('.title').innerHTML = title;
    return this.getTitle();
  }
});

})(Skidooxoo.Scriblr);
[/javascript]

We add the @Post@ function to the @Scriblr@ object. @Post@ is a constructor, so we can instantiate it, giving it an HTML element containing a post DOM element as the only parameter. The constructor will set the div as a property on the @Post@ instance. We also add a class method to @Post@ - @findPosts@ - which searches the DOM for elements with the class &quot;post&quot; and instantiates each of them and returns these new objects in an array. Each of the objects returned in the array has two instance methods which gets and sets the post's title.

@Object.extend@ is a Prototype function that extends the object in its first argument with the properties of the second object. In this code we're extending @Post@ and @Post.prototype@, effectively creating &quot;class&quot; and &quot;instance&quot; methods. We're also passing @Skidooxoo.Scriblr@ into an anonymous function because then we can use the argument variable instead of having to spell out our stupid Web 2.0 name all the time.

Of course, we want to keep these objects somewhere. This can be, for example, on the @Scriblr@ object. But we don't want to do this manually; we want it to happen as soon as the page has loaded. We'll simply write a function that's called when the DOM is ready.

[javascript]
Skidooxoo.Scriblr.Post.onReady = function(){
  Skidooxoo.Scriblr.posts = Skidooxoo.Scriblr.Post.findPosts();
};
[/javascript]

We then call this function at the appropriate time. I like to keep each of the different elements in separate files, so if we put this in @posts.js@, we'll just add an event handler at the bottom of this file:

[javascript]
Event.observe(window, 'dom:loaded', Skidooxoo.Scriblr.Post.onReady);
[/javascript]

As comments in our app won't appear unless there's also a post, we'll add the @Comment@ class to the @posts.js@ file too. Let's create the class for comments. Assume the following code is inside the same @(function(Scriblr){ ... })(Skidooxoo.Scriblr)@ closure as @Post@ above.

[javascript]
Scriblr.Comment = function(element, post){
  this.element = element;
  this.post = post;
};

Object.extend(Scriblr.Comment.prototype, {
  getName: function(){
    return this.element.down('.name').innerHTML;
  },
  setName: function(name){
    this.element.down('.name').innerHTML = name;
    return this.getName();
  }
});
[/javascript]

All comments belong to a post, so we have to pass this as an additional parameter when creating these objects. The @post@ object is the encapsulated div created with the @Post@ constructor. Knowing that comments belong to a post, we also know that a post has zero or more comments. We can make it the post's responsibility to find its own comments by adding an instance method and rewriting the constructor:

[javascript]
Scriblr.Post = function(element){
  this.element = element;
  this.comments = this.findComments();
};

Scripblr.Post.prototype.findPosts = function(){
  var post = this; //To have access to &quot;this&quot; in the iterator function
  return this.element.select('.comments .comment').map(function(div){
    return new Scriblr.Comment(div, post);
  });
};
[/javascript]

Now each post will know how to find its comments and it will add them to the @comments@ property when it's created. Each comment will also know about its context (the post it belongs to) and be able to get to it.

At this point we can access all posts and their comments through JavaScript. When you start adding more methods to the objects and start performing these and messing around with the objects from the Firebug console you'll appreciate how much more useful and flexible this approach is than just throwing everything into one big, procedural file that leaves no traces from what it's been doing.

h3. Hijax those links and forms

Let's add some more methods to the @Post@ objects. The rating feature is an obvious candidate for &quot;Ajaxification&quot;, so we'll add some methods to deal with that. First, let's add a @rate@ method which submits a rating to the server and gets back the average rating.

[javascript]
Scriblr.Post.prototype.rate = function(rating, fn){
  new Ajax.Request('/posts/'+this.getID()+'/rate/'+rating, {
    onSuccess: function(res){ fn(res.responseText); }
  });
};
[/javascript]

The method takes a rating and a callback function which receives the average rating. This is a very na&#239;ve implementation of course and should be made more robust for a real application. The @getID@ method is an unimplemented method which extracts the post's ID from the @id@ attribute of its div element, so &quot;post_1&quot; would result in &quot;1&quot;.

The next step is to attach the event handlers to the rating links (that already work without JS). I usually add an @observe@ method to objects that does these things:

[javascript]
//Call observe on creation
Scriblr.Post = function(element){
  //...
  this.observe();
};

Scriblr.Post.prototype.observe = function(){
  var post = this;//Keep in scope
  this.element.select('.rating li a').each(function(a){
    a.observe('click', function(){
      post.rate(a.innerHTML, function(average){
        post.element.down('.rating').replace('&lt;p&gt;Average: '+average+'&lt;/p&gt;');
      });
    });
  });
};
[/javascript]

Now every post will add event handlers to its rating links which will call the post's @rate@ method and receive the average rating and replace the rating links with it. This whole rating implementation is very incomplete and bolted on, but it shows how the post object can take care of these things from within, always knowing about and having access to its own state. It _can_ be much more elaborate than this, and in my experience that's when it pays off to be this structured.

h3. Don't mess with my style

When we start hijaxing everything on the page we should also let the user know that something's going on or that the state of an element has changed. This is often done by giving the user visual clues such as changing the colour of the element. What we shouldn't do, however, is modify the element's style directly. This is by definition obtrusive, and we're trying to be &lt;em&gt;un&lt;/em&gt;obtrusive. The unobtrusive and semantic way of doing this is to add or remove class names to the element.

Let's say we want to let users notify the blog owner if they think a comment is offensive or abusive. We add a link in each comment to do this (on the server) and hijax it to not force the user out of his reading context. We need a way to notify him that the notification has been submitted. The solution is simple; we just add a class name to the comment element and let the style sheet decide what this will look like visually.

[javascript]
Scriblr.Comment.prototype.notify = function(){
  var comment = this;
  new Ajax.Request(this.generateNotificationURL(), {
    onSuccess: function(){
      comment.element.addClassName('notified');
    }
  });
};
[/javascript]

h3. The End

If you have any interesting techniques that you'd like to share, please leave a comment!</body>
  <created-at type="datetime">2008-01-16T20:45:20-05:00</created-at>
  <id type="integer">23</id>
  <parent-id type="integer">12</parent-id>
  <published type="boolean">true</published>
  <slug>unobtrusive_javascript_with_rails</slug>
  <summary>More and more people are jumping on the &quot;unobtrusive JavaScript&quot; bandwagon these days. That's not surprising, because it's a very nice wagon to be on. Hopefully we'll soon experience the shift from bad to good practises that we did with CSS a few years back. Then it was about separating presentation from content, today it's about separating behaviour from content (and presentation).

People are &quot;rediscovering&quot;:http://simon.incutio.com/slides/2006/etech/javascript/js-reintroduction-notes.html JavaScript, and this new &quot;Ajax&quot; thing seems to have become pretty popular too. They're eager to move their code out of the HTML and into re-usable objects in separate files. But I feel like there's something missing from this whole renaissance: _structure_. Everyone eventually discovers this when their files grow large and unmaintainable and code is duplicated for different parts of a site.

This article is about structuring JavaScript code, encapsulating elements on the page and creating reusable (&quot;DRY&quot; if you're an expensive consultant) code. It's just explaining what I've personally found to be best practise in these areas, and the techniques probably have room for improvement. It does not deal with JavaScript itself or how to use any of the JS/Ajax libraries. And despite the title, it has as good as nothing to do with Rails. I thought it would, but the techniques turned out to be very generic. That's not to say it's not useful in combination with Rails, because it definitely is. A lot of the &quot;encapsulated elements&quot; will correspond directly to models in Rails (or tables in the database, if you wish).

Ok, I'm rambling now, let's just get on with it already. Here are my _Top Tips_ for creating extensible, maintainable, reusable and understandable website overlays with JavaScript (&quot;Ajax apps&quot;):</summary>
  <title>Unobtrusive JavaScript with Rails</title>
  <updated-at type="datetime">2008-03-15T17:04:22-04:00</updated-at>
</page>
