Sneaky Abstractions

Subscribe to my Feed, follow me on , recommend me on Working With Rails or see my code on GitHub

Buttons? What buttons?

Posted on July 31, 2008 18:52 Tagged with button_to, javascript, rails, unobtrusive.

So we’re all nice little RESTful developers and everything is working great. There’s just one little annoyance. To be able to let our users create, modify or delete resources we have to use a form. But then we have all these buttons messing up our rounded corners, because form elements aren’t treated as every other element in the DOM by the browsers and are difficult to style. Rails has a hack wherein it can make a link appear as though it works with POST requests. It does this by adding a hidden form which is submitted when the link is clicked. This works fine, but it’s obtrusive and – dare I say – not very DRY. If we turn this on its head, we can make it both unobtrusive and DRY.

To do this, we start out with the form instead of the link. This will make it unobtrusive, because a user with no JavaScript will just get a button instead. The JavaScript will hide the form and add a link which submits it when clicked. To make it DRY, we just extract this JavaScript into an external file:

document.observe('dom:loaded', function(){
  $$('form.button-to').each(function(form){
    var link = new Element('a', {href:'#', 'class':'button-to'});
    link.update(form.down('input[type=submit]').value);
    link.observe('click', function(e){
      e.stop();
      form.submit();
    });
    form.insert({after:link});
    form.hide();
  });
});

We don’t even have to worry about the server side which spews out the HTML, because this works with the existing button_to helper. All forms with the class “button-to” will be replaced by links.

Now our apps can be unobtrusive, DRY and pretty.

Zippy: rubyzip for dummies

Posted on July 30, 2008 08:21 Tagged with zippy, zip, rubyzip, ruby.

Ok, so I had to dynamically create zip files the other day. No biggie, rubyzip handles that very well. It’s just that 90% of the time it took was spent reading through the docs to figure out exactly which spells I had to cast to make it do what I wanted. And I’ve done it before, too. Now, it says right there on the first page – almost like a disclaimer – that “the basic API is modeled after the classes in java.util.zip from the Java SDK”, but seeing things like ZipOutputStream and ZipStreamableStream gives me flashbacks to Programming 101 and travelling salesmen; why couldn’t it be more like, say, Hash? This is Ruby damnit, we don’t actually want to program, we want to tell the computer what to do and then it just does it. Kind of like this:

Zippy.create 'funny.zip' do |zip|
  zip['README'] = 'Fun pics 4 u'
  Dir['pictures/*.jpg'].each do |filename|
    zip[filename] = File.open(filename)
  end
end

You can probably guess by now that I’m not just making this stuff up as I go. Zippy is a (thin) wrapper around rubyzip which has a more Ruby-like interface. It happily ignores edge cases – you know, like handling directories – and focuses on the most common operations.

Zippy.open 'funny.zip' do |zip|
  log "Changing README, was \"#{zip['README']}\""
  zip['README'] = 'Amusing photographs of cats'
end

Zippy.create('reports.zip', 'report.pdf' => generate_pdf, 'README' => 'Latest reports')
puts Zippy['reports.zip', 'README']
Zippy['reports.zip', 'another_report.pdf'] = generate_another_pdf

One of the things I had to deal with was having to give rubyzip a filename, although I wasn’t going to save the file to disk but send it as a response to a request in a Rails app. Zippy doesn’t need an explicit filename (it will still save it to a temporary file):

Zippy.new('foo.txt' => 'bar').data

In fact, if you add it as a gem dependency in your Rails app,

config.gem "zippy", :source => "http://gems.github.com", :lib => "zippy"

it has a little plugin which registers a template handler for the extension zipper, so you can generate zips on the fly:

#galleries_ontroller.rb
class GalleriesController < ActionController::Base
  def show
    @gallery = Gallery[params[:id]]
    respond_to do |format|
      format.html
      format.zip
    end
  end
end

#galleries/show.zip.zipper
zip['gallery.txt'] = "#{@gallery.title}\n\n#{@gallery.description}"
@gallery.photos.each do |photo|
  zip["photos/#{photo.to_param}.jpg"] = File.open(photo.filename)
end

Barby 0.2: Now with added dimension

Posted on July 16, 2008 19:15 Tagged with barby, ruby, barcodes.

QR Code

Barby 0.2 has support for two-dimensional barcodes, and all existing outputters have been updated to work with 2D. The first supported 2D symbology is QR Code which is quite popular for mobile phone implementations and big in Japan. It uses the excellent RQRCode library by Duncan Robertson.

require 'barby'
require 'barby/outputter/png_outputter'

qrcode = Barby::QrCode.new('2D power')

File.open('qrcode.png', 'w') do |f|
  f << qrcode.to_png
end

To update or install:

sudo gem install barby

The implementation of 2D support is pretty simple: 2D barcodes are considered to be 1D barcodes on top of each other. So if you’re writing a class for a 2D symbology, the difference is that your encoding method must return an array of strings instead if a single string:

class My2DBarcode < Barby::Barcode2D

  def endoding
    ['010011', '011001', '110001']
  end

end

If you’re writing an outputter that is capable of handling 2D barcodes, you just have to keep this in mind. You can use the two_dimensional? method to check if a barcode is 2D:

class MyOutputter < Barby::Outputter

  def to_something
    if barcode.two_dimensional?
      encoding.inject(""){|s,line| s + do_something_with(line) }
    else
      do_something_with(encoding)
    end
  end

end

Unobtrusive Ajax patterns: revealers

Posted on July 15, 2008 08:18 Tagged with unobtrusive, javascript, ajax patterns.

The revealer pattern has two components: The revealer and the target. This can be a one-to-many or even many-to-many relationship, but the core functionality consists of the revealer element showing a target element when an event happens. It’s a very simple but yet versatile pattern.

The goal in this little tutorial is to be able to mark elements as either revealers or targets by using class names. We want to be able to write this HTML,

<h3 class="revealer reveals:more-info">More information</h3>
<p id="more-info">
  This is the extra information.
</p>

and it will automatically hide the “more-info” element until the revealer (the h3) is clicked.

First, we find all elements that have the class name "revealer":

$$('.revealer').each(function(revealer){
  //...
});

Then, we extract the target element IDs from the class attribute and find the corresponding elements in the DOM:

$$('.revealer').each(function(revealer){
  var className = revealer.readAttribute('class'),
      re = "reveals:([^ ]+)";//We need both a "normal" and a "global" version

  //Find the target elements for this revealer
  var targets = (className.match(new RegExp(re, 'g')) || []).map(function(str){
    return $(str.match(new RegExp(re))[1]);
  });

});

We search through the class attribute with a regular expression. Note that we use both a “normal” and a “global” version of it. The global version is used to find all occurrences, which are fed into a map where the ID part (after “revealer:”) is extracted. The ID is used to find the element in the DOM.

Now that we have the revealer and the targets, we just need to add some initial class names to them and attach an event handler to the revealer element which toggles some class names on the targets:

$$('.revealer').each(function(revealer){
  // [...]

  //Add the initial class names
  revealer.addClassName('activated');
  targets.invoke('addClassName', 'revealable');
  targets.invoke('addClassName', 'concealed');

  revealer.observe('click', function(e){
    //Not all elements have a default action,
    //but we stop it to be on the safe side
    e.stop();
    targets.invoke('toggleClassName', 'revealed');
    targets.invoke('toggleClassName', 'concealed');
  });
});

We add the initial class names to be able to target them in the style sheet. When done this way, the CSS rules will only be applied if the user has JavaScript running. The targets have an initial class name “concealed” which is used to target them in the style sheet:

/* The cursor will be shown only when JS is available */
.revealer.activated {
  cursor: pointer;
}

.revealable {

}

  .revealable.concealed {
    display: none;
  }

  .revealable.revealed {
    
  }

In this example only the “concealed” class is used to hide the element. We could have set the target’s initial display value to “hidden” and then changed it when the element receives the “revealed” class, but this isn’t optimal. We have no idea of knowing what the initial value of the property was; it could be “block”, “inline” or something else entirely. By using the reverse logic, that is we hide the element when it has the class name “concealed”, it will automatically go back to its initial value when that class name is removed.

Now, we can see if our code works. I’ve added a blue border to revealer elements and a green border to all targets. As a meaningless gimmick, the second part of the tutorial is contained inside a revealable target with the heading as the revealer :)

Many to many

So far, the revealer element has been the one containing the link to its related targets, but we can also make it the target’s responsibility to tell us which element(s) it can be revealed by. To make this happen, we reverse the logic:

$$('.revealable-target').each(function(target){
  var className = target.readAttribute('class'),
      re = "revealed-by:([^ ]+)";

  var revealers = (className.match(new RegExp(re, 'g')) || []).map(function(str){
    return $(str.match(new RegExp(re))[1]);
  });

  target.addClassName('revealable');
  target.addClassName('concealed');
  revealers.invoke('addClassName', 'revealer');
  revealers.invoke('addClassName', 'activated');

  revealers.each(function(revealer){
    revealer.observe('click', function(e){
      e.stop();
      target.toggleClassName('revealed');
      target.toggleClassName('concealed');
    });
  });
});

First we find all elements with the class name “revealable-target”, then search its class attribute for “revealed-by:foo” to get the revealer elements by ID. Then we add an observer to each revealer element in the same way we did in the reverse example.

Below is an example where the revealable target contains the information about its revealer as opposed to the revealer containing the target information.

This element isn’t marked as a revealer

I’m also a revealer for the above target

Unobtrusive Ajax patterns: star rating

Posted on July 11, 2008 11:27 Tagged with unobtrusive, javascript, ajax, rating, ajax patterns.

The “star rating” is now becoming a familiar pattern on the web, where users can rate an item using by giving it a number of “stars”, and their choice gets sent back to the server asynchronously.

It’s quite easy to add behaviour on top of this pattern unobtrusively. Let’s say we have this HTML:

<ul class="rater">
  <li><a href="/rate/1">1</a></li>
  <li><a href="/rate/2">2</a></li>
  <li><a href="/rate/3">3</a></li>
  <li><a href="/rate/4">4</a></li>
  <li><a href="/rate/5">5</a></li>
</ul>

This works without any JavaScript and can be styled quite easily. We’re not going to mess around much with the DOM structure, all we really need to do is hijack the links and make them work asynchronously. We can hook into each “rater” by using its class name:

$$('ul.rater').each(function(rater){
  //...
});

So, to hijack the links, we use the event delegation technique, which means we attach a single handler to the rater element:

$$('.rater').each(function(rater){
  
  rater.observe('click', function(e){
    var target = e.element();//The element which was clicked
    if (!rater.hasClassName('rated') && target.tagName.toLowerCase() == 'a') {
      //We're in a link element, stop the default action
      e.stop();
      // [...]
    }
  });

});

In the handler, we check if the element that was clicked is a link. If not, we don’t do anything, but if it is a link, we can proceed by sending an asynchronous request:

// [...]
      new Ajax.Request(target.readAttribute('href'), {
        onLoading: function(){
          rater.addClassName('loading');
        },
        onSuccess: function(res){
          //Add "selected" class to all previous list items
          target.up().previousSiblings().invoke('addClassName', 'selected');
          //Add to clicked list item
          target.up().addClassName('selected');
          rater.addClassName('rated');
        },
        onComplete: function(){
          rater.removeClassName('loading');
        }
      });
// [...]

The request takes its URL from the link’s href attribute. When it gets sent (onLoading), the rater receives the class name “loading”, which is removed again when the request completes (onComplete), no matter if the request was successful or not. If it was successful, we loop through the rater’s li elements up to and including the selected and add the class name “selected”.

We use CSS to make it look like a rating widget, using the “selected” class to show visually which rating the user selected:

.rater {
  list-style-type: none;
  overflow: hidden;
  font-size: 150%;
  font-weight: bold;
}

  .rater li {
    float: left;
  }

    .rater li a {
      display: block;
      text-decoration: none;
      color: #ff6 !important;
      background: #666;
      padding: 0 0.25em
    }

    .rater li.selected a {
      color: #ff3 !important;
      background-color: #333;
    }

In this example, I’ve replaced the numbers with stars – ★ -you might not have the right font to see it, but you might as well use background-image in the CSS. You’ll also notice that on mouseover only the current element gets highlighted; there’s not really any good way to do this with CSS, so you probably have to attach onmouseover and onmouseout handlers to the rater to add the “selected” class name.

The example doesn’t actually submit anything, but uses a timeout to simulate the request.

Unobtrusive Ajax patterns: the modal dialog box

Posted on July 09, 2008 13:18 Tagged with modal, javascript, ajax patterns.

(Another one that doesn’t deal with Ajax directly, but is very useful when combined with it. Example at the bottom of the article)

The modal dialog is very familiar to the desktop environment. It’s the window that pops up from the main application window to ask you something or let you know something happened. On the web, there is one particular form of the modal dialog that’s popular, the “lightbox”.

Ok, so how do we create a modal dialog for a web site? It’s quite simple really, you just need an element that can be switched “on” or “off” and some CSS to make it appear the way we want. We’ll keep the JavaScript used to control the dialog inside a global Modal object that encapsulates the element so we don’t actually mess around with it directly when using the dialog.

We create the element on page load, and insert it into the body of the document:

var Modal = (function(){

  var box = new Element('div', {id:'modal-dialog'});
  $$('body').first().insert({bottom:box});

  return {
    element: box,
    show: function(){},
    hide: function(){},
    update: function(){}
  };

})();

The Modal object has three basic methods: show to open the dialog, hide to close it and update to update its contents. These abstract away what actually goes on when using the dialog, so if you want to change how something is done later, it won’t break code that relies on it.

The show and hide methods are quite simple. Let’s throw in a toggle method too, while we’re at it.

//...
return {

  show: function(){
    box.addClassName('active');
  },

  hide: function(){
    box.removeClassName('active');
  },

  toggle: function(){
    this[this.isActive() ? 'hide' : 'show']();
  },

  isActive: function(){
    return box.hasClassName('active');
  }

};
//...

The dialog is considered to be open when it has the class name “active”. This will be targeted with CSS to make it appear and disappear. Note that we’re using the box variable which is the DOM element representing the dialog. The methods have access to it because they’re inside a closure where box is a local variable. This in effect makes it private (although it’s mostly for convenience, as we’re actually letting the outside access it by adding it to Modal.element).

The update method is also quite simple, it basically acts as a proxy to the element’s update method:

update: function(){
  box.update.apply(box, arguments);
}

Now, to display a message in the modal, we can do this:

Modal.update('I just popped up to say I love you');
Modal.show();

We can do better, though. The update method will almost always be called just before show, so we can reduce that to one step by allowing show to pass the content on to update:

show: function(){
  if (arguments.length) { this.update.apply(this, arguments); }
  box.addClassName('active');
}
Modal.show('I just popped up to annoy you');

Now that we have the functionality ready, we just have to add some style to make it look like a modal box:

#modal-dialog {
  display: none;
}

  #modal-dialog.active {
    display: block;
    position: fixed;
    top: 5em;
    left: 5em;
    background: #fff;
    border: 0.5em solid #ccc;
    padding: 2em;
  }

This can be enhanced in a number of ways (like making it display in the center of the page), but the needs will vary from site to site and have to be added on a case-by-case basis. There’s one thing we can add though, that can be considered quite universal: The ability to close the dialog from within the dialog itself.

To do this, we just need to hook up an element inside the dialog to the Modal.hide method. To make it easy to use, we’ll do this to every element with a class name of modal-dialog-close. This way you can just add this class name to content that gets added to the dialog, and it will work automagically.

The hooking up happens in the update method, after updating the element’s contents:

update: function(){
  box.update.apply(box, arguments);
  box.select('.modal-dialog-close').each(function(close){
    close.observe('click', function(e){
      e.stop();
      Modal.hide();
    });
  });
}

Done

The entire script now looks like this:

Modal = (function(){

  var box = new Element('div', {id:'modal-dialog'});
  $$('body').first().insert({bottom:box});

  return {

    element: box,

    show: function(){
      if (arguments.length) { this.update.apply(this, arguments); }
      box.addClassName('active');
    },

    hide: function(){
      box.removeClassName('active');
    },

    toggle: function(){
      box[this.isActive() ? 'removeClassName' : 'addClassName']('active');
    },

    isActive: function(){
      return box.hasClassName('active');
    },

    update: function(){
      box.update.apply(box, arguments);
      box.select('.modal-dialog-close').each(function(close){
        close.observe('click', function(e){
          e.stop();
          Modal.hide();
        });
      });
    }

  };

})();

An OpenID blank slate Rails application

Posted on July 07, 2008 20:23 Tagged with openid, rails, authentication.

OpenID is awesome, but getting a Rails application up and running with OpenID authentication can be a bit cumbersome. After having done that a few times, I’ve created an OpenID starter kit which will let you (and others) log in to it using OpenID. It uses the open_id_authentication plugin, which takes away a lot of the nitty-gritty of authentication.

It uses a couple of gems, which can be installed using

rake gems:install

When you have those installed, all you have to do is copy the config/database.yml.example file to config/database.yml, check the settings (default Rails config with SQLite) and run rake db:migrate. Then, just run script/server and log in and out as much as you want.

A simple custom event system

Posted on July 07, 2008 10:01 Tagged with events, javascript.

Say you have a lot of elements on a page that all depend on each other in some way. When one element changes, you want to let the others know so they can take the appropriate action. You could have the changed element perform the action on the dependent element, but that could get messy real fast. A better solution is to leave them decoupled and have the changed element send an event or a message (not sure what the right terminology would be here, if I use both just assume they mean the same thing). The dependent element could subscribe to this particular event and perform the necessary action when it receives it. This is a tried and tested solution that’s surprisingly simple and easy to implement. In this article I’ll go through implementing a simple messaging system in JavaScript (I’m talking DOM elements here).

This is what we want to be able to do:

var obj = {};

Messages.subscribe('a message', function(bar){
  obj.foo = bar;
});

Messages.fire('a message', 'it works');

alert(obj.foo); //Alerts "it works"

First, we need an object in which to keep everything:

Messages = {
  subscriptions: {}
};

Let’s implement the subscription logic first. This is a function that let’s something subscribe to a particular event (identified by a string in this case), and pass a function that gets run when that event happens.

Messages.subscribe = function(message, callback, scope){
  if (!this.subscriptions[message]) { this.subscriptions[message] = []; }
  this.subscriptions[message].push({callback:callback, scope:scope});
};

This function adds an object containing the callback function (which gets run when the event it’s subscribed to happens), and an optional scope in which the callback gets run. Each message can have several subscriptions, so they’re contained in an array. The array for a particular message is created if it doesn’t already exist.

Now that we can subscribe to events, we need a way to fire one too; it would be pretty useless otherwise. The fire method will check the subscriptions object to see if there are any subscriptions for the event that’s getting fired.

Messages.fire = function(message){
  var i, s, args, subscriptions;
  if (subscriptions = this.subscriptions[message]) {
    args = Array.prototype.slice.apply(arguments, 1);
    for (i=0; i<subscriptions.length; i++) {
      s = subscriptions[i];
      s.callback.apply(s.scope || window, args);
    }
  }
};

Now we can fire an event, and all the subscription callbacks for that event will be run. Note that we pass the rest of the arguments given to the fire method on to the callback.

Finally, it should be possible to unsubscribe from an event. What this does is it goes through the array for the event specified and removes any subscriptions whose callback matches the passed function:

Messages.unsubscribe = function(message, callback){
  var i, s;
  if (s = this.subscriptions[message]) {
    for (i=0; i<s.length; i++) {
      if (s[i].callback == callback) { s.splice(i,1); }
    }
  }
};

- Ok, that’s neat, but what can I do with it?

Well, let’s say you’re a little weird and keep one of those blahgs everyone’s talking about. You have a content area where you list your posts in full and a sidebar where you have links to the latest posts for your readers’ convenience. You’ve also added some of that nifty Ajax technology to be able to delete a post asynchronously from the page. What if you delete a post that also is in the sidebar list? The list will be out of sync and display a link to a post that no longer exists. You could make the delete action on the post go look in the sidebar to see if it’s in there too, and remove it, but then you’ve coupled those two previously separate elements, and coupling is bad, says the experts. I guess you can see where I’m going with this.. The sidebar list can subscribe to an event that gets fired by the post’s delete action!

Let’s say this is the “content” part:

<div class="posts">
  <div class="post" id="post_2">
    <h3>Post 1</h3>
    <p>This is post 1</p>
  </div>
  <div class="post" id="post_2">
    <h3>Post 2</h3>
    <p>This is post 2</p>
  </div>
</div>

You’ll notice there aren’t any “delete” links or buttons there, we’re imagining that they’ve somehow been added dynamically. The corresponding sidebar list looks like this:

<ul class="posts">
  <li id="sidebar_post_1"><a href="/posts/1">Post 1</a></li>
  <li id="sidebar_post_2"><a href="/posts/2">Post 2</a></li>
</ul>

This is the function that deletes a post from the “content” part, extremely simplified for clarity:

function deletePost(post){
  sendRequest('DELETE', '/posts/'+post.id);
  post.removeFromDOM();
  Messages.fire('post was deleted', post.id);
};

After deleting the post and removing it, the action fires a message “post was deleted”. This message is subscribed to by the sidebar list:

Messages.subscribe('post was deleted', function(id){
  removePostFromSidebar(id);
});

Again, extremely simplified. When a post gets deleted, a message gets sent, along with the ID of the post that was deleted. The sidebar list subscribes to this message, so its callback function gets run, and this function in turn removes the corresponding post from the list.

Unobtrusive Ajax patterns: tabs

Posted on July 03, 2008 10:32 Tagged with tabs, javascript, ajax patterns.

(Ok, there’s not one bit of Ajax in this, but I’m just trying to keep it consistent :)

I guess you know what tabs are: There are two main components, the tabs themselves and the panes they control. To “tabify” an HTML element, we need to know which element is the container (like a form) and which elements are the tab panes and the titles to be used in the tabs. Let’s use a form as an example container:

<form class="tabs">
  <fieldset class="tab">
    <legend class="title">Tab 1</legend>
    <p><label>Koda</label><input type="text" /></p>
    <p><label>Basanda</label><input type="text" /></p>
  </fieldset>
  <fieldset class="tab">
    <legend class="title">Tab 2</legend>
    <p><label>Bosoya</label><input type="text" /></p>
    <p><label>Tikki</label><input type="text" /></p>
  </fieldset>
  <p class="button">
    <input type="submit" value="Ok" />
  </p>
</form>

Here you can see that the fieldsets are marked as tabs. Actually they’re tab panes, we’ll generate the tabs from the title elements (legend).

document.observe('dom:loaded', function(){

  //Find all elements with the "tabs" class (the containers)
  $$('.tabs').each(function(container){
    var tabs = container.select('.tab');//Panes
    //Create a list to contain the tab elements
    var tabList = new Element('ul', {'class':'tablist'});
    //Go through each tab pane
    tabs.each(function(tab){
      var li = new Element('li');//A tab
      li.update(tab.down('.title').innerHTML);
      //Observe the click event, this changes the active tab
      li.observe('click', function(){
        //First, inactivate all tabs
        tabList.select('li').invoke('removeClassName', 'active');
        //Then, activate this tab
        li.addClassName('active');
        //Inactivate all tab panes
        tabs.invoke('removeClassName', 'active');
        //Activate the associated tab pane
        tab.addClassName('active');
      });
      tabList.insert({bottom:li});//Add the tab to the list
    });
    container.addClassName('enhanced');//Make it targetable with CSS
    container.insert({top:tabList});//Add the tablist to the top of the container
    //Activate first tab
    tabs.first().addClassName('active');
    tabList.down('li').addClassName('active');
  });

});

Now all you need to do is add some CSS:

.tabs .tablist {
  list-style-type: none;
  margin: 0 !important;
  overflow: hidden;
}

  .tabs .tablist li {
    float: left;
    background: #ccc;
    border: 1px outset #ccc;
    border-bottom: 1px solid #bbb;
    color: #333;
    padding: 0.25em;
    margin-right: 2px;
  }

    .tabs .tablist li:hover {
      cursor: pointer;
      text-decoration: underline;
    }

    .tabs .tablist li.active {
      background-color: #ddd;
      border-color: #ddd;
    }

.tabs.enhanced .tab {
  display: none;
  background: #ddd;
  border: 1px outset #ddd;
  border-top: none;
  padding: 1em;
}

  .tabs.enhanced .title {
    display: none;
  }

  .tabs.enhanced .tab.active {
    display: block;
  }

And then you have tabs:

Tab 1

Tab 2

Tab 3

Tab 4

Of course this can be organised and abstracted more to make it seem a bit more object-oriented, but that’s the basics of it.

Decoupling event handlers

Posted on July 01, 2008 10:26 Tagged with javascript, events.

Have you ever wondered how you can “fire” an event on an element that you’ve attached a listener to, so you can implicitly perform that action? I know I have, but I don’t anymore, because it taught me a lesson: The action performed (a function) when an event happens on an element should be decoupled from the actual event. We all know that coupling is bad, so how do you decouple something like this, for example?

$('add_new_text_field').observe(function(e){
  e.stop();
  var form = e.up('form');
  var input = new Element('input', {type:'text',name:'something'});
  form.insert({bottom:input});
});

Well, it’s easy! Just identify what the action performed by the handler is, and separate it from the handler. In this case, it’s adding a new text field to a form. In the example above, the action is part of the handler for the element with the id “add_new_text_field”. If you want to re-use this functionality, you’re out of luck.

The action, extracted and decoupled from the handler, looks like this:

function addNewTextField(form){
  var input = new Element('input', {type:'text',name:'something'});
  form.insert({bottom:input});
}

Now, the event handler is much simpler:

$('add_new_text_field').observe(function(e){
  e.stop();
  addNewTextField(e.target.up('form'));
});

And you can re-use the action in another handler! Don’t you just love anonymous functions?

1 2 3 4 5

I'm telling y'all it's sabotage!!