Simplify your JavaScript with CoffeeScript

This article first appeared in issue 221 of .net magazine – the world's best-selling magazine for web designers and developers.

JavaScript isn’t very readable, and unreadable code is hard to maintain. Compared with Ruby or Python, there are brackets, braces and quotes everywhere. Often, there’s more syntax than software.

Enter CoffeeScript. It isn’t a framework, but instead compiles to runnable JavaScript. You write CoffeeScript, compile it, and out pops clean, tight JavaScript ready for the browser. You get optimised JavaScript, but work with clean, understandable, maintainable code.

CoffeeScript starts to make real sense once you’ve written some, so let’s get going. First, we’re going to install the CoffeeScript compiler, and have it convert our CoffeeScript files into JavaScript that we can load in our browser.

To get CoffeeScript installed on your development machine, you’ll need a *nix-like environment, a text editor, a terminal, and a browser to check the results. First we install node.js (a JavaScript runtime that CoffeeScript needs to do its magic). We then install node’s package manager (npm) and use that to install CoffeeScript itself. On OS X, the simplest way to do this is with homebrew. Make sure you have XCode installed, then follow the instructions to install homebrew, or just open a terminal session and type:

/usr/bin/ruby -e "$(curl -fsSL"Install node.js: brew install node

then npm and CoffeeScript:

curl | sh npm install -g coffee-script

If you’re running Linux, your package manager can install node, then install npm and CoffeeScript. If, on the other hand, you’re using Windows, try following Matthew Podwysocki’s instructions at CodeBetter. This tutorial is all about the CoffeeScript syntax, so let’s keep the project goal simple: enable client-side validation on a form. Here’s our form, in index.html:

<form id="contact_form"> <ol> <li> <label>Name</label> <input type="text" name="name"> </li> <li> <label>Email</label> <input type="email" name="email"> </li> <li> <label>Enquiry</label> <textarea name="enquiry"></textarea> </li> </ol> <ol><li><input type="submit"></li></ol> </form>

We also have a directory, script, containing a blank file called

Better looking: CoffeeScript's code is more understandable than vanilla JavaScript

Boiling the water

Let’s write CoffeeScript! Open and add the following:

required_field_names = ['name','email']

Just JavaScript so far, but it’s a start. Let’s compile Open a terminal and cd into the script directory:

cd public/script

CoffeeScript’s coffee utility can compile the file:

    coffee -c

The -c flag tells coffee to compile the CoffeeScript to a file. Without it, coffee compiles and runs the file. -c will write JavaScript out to a file called form.js, in the same directory as the file. Let’s look at form.js:

(function() { var required_field_names; required_field_names = ['name', 'email']; }).call(this);

What’s happened? The required_field_names variable has been scoped by var, and the whole script has been wrapped in a namespace. This protects us against one of the most common sources of bugs in JavaScript: accidental global variables. In CoffeeScript, variables are local by default, instead of global as in regular JavaScript. If you’ve never worried about scoping in JavaScript before, you’re very lucky. If you have, then this is a lifesaver.

The first sip

Let’s include our JavaScript in index.html:

<head> <script src="/script/form.js"></script> </head>

and update the form to call a function when submitted:

<form onsubmit="return validate(this);">

Let’s declare our required fields array in this validate function and return false to prevent the form submitting while developing. In JS, we might do:

var validate = function(form) { var required_field_names = ['name','email']; return false; }

With CoffeeScript, we can lose var call. Additionally:

function ( args ) {


(args) ->

and in place of braces, we use indentation to define the function’s content:

validate = (form) -> required_field_names = ['name','email'] return false

Notice we’ve dropped trailing semi-colons too. One last trick: CoffeeScript returns the result of the last expression in a function, so we can lose the return:

validate = (form) -> required_field_names = ['name','email'] false


coffee -c

and look at form.js to check. Reload our form, hit Submit and ... the form submits. What’s gone wrong?

CoffeeScript creates everything in a namespace and with local scope. That means that while validate is available to anything within, it’s not available outside that file, so onsubmit can’t access it. This prevents another JavaScript file redefining validate, but isn’t very helpful here.

To make the function global, we just change the assignment:

window.validate = (form) ->

This attaches validate to the window object, making it globally available. Compile again:

coffee -c

then reload, submit the form and ... no submission! Compiling on every change is getting tedious, so let’s use the ‘watch’ functionality instead:

coffee -cw *.coffee

-w starts the compiler and leaves it running, compiling any changed file matching the *.coffee pattern. With this running, we can edit our CoffeeScript files, save and reload the compiled version in the browser almost immediately.

One-window development: Coda is great for developing CoffeeScript - the terminal can just hum away in the background, compiling for the browser

Grinding the beans

We have a list of required field names and a submit handler to check them. To grab all the fields with the names given, we might do the following in JS:

var required_fields = []; for ( var name in required_field_names ) { var field = form.elements[name]; required_fields.push(field); }

We could transliterate into CoffeeScript: lose the braces, use indentation. We can also lose brackets around arguments passed to functions too, just like in Ruby, giving:

required_fields = [] for name in required_field_names field = form.elements[name] required_fields.push field

We can go one better:

required_fields = for name in required_field_names return form.elements[name]

In other words, for() will return an array containing the return values from each iteration. Remember, we don’t need an explicit return in CoffeeScript, so:

required_fields = for name in required_field_names form.elements[name]

The for block is now just a single statement, and CoffeeScript gives us the following trick, putting the condition after the statement we want to run:

required_fields = ( form.elements[name] for name in required_field_names )

This reads like a sentence:

> Required Fields are the form elements for each name in Required Field Names

We’ve distilled five lines of JavaScript down to a single, clean line of code that expresses precisely what it does. validate itself is now four lines long. The compiled JavaScript is sitting at around 18 lines of bullet-proof, tight and memory-efficient code. There’s a lot to like about CoffeeScript.

Pure and simple: here's our final source code (on the left). It's readable code, which means it's maintainable code

Checking the roast

We have an array of input elements, so let’s check that each has a value. In JavaScript, we could do this:

var errors = []; for ( var field in required_fields ) { if ( field.value == '' ) { errors.push(; } }

This would collect an array of bad field names. Let’s tidy this up:

errors = [] for field in required_fields if field.value == '' errors.push field_name

This doesn’t seem to be much of a saving. Notice that there’s only a single statement in the if block. This means we can do the same trick we managed with the for block – putting the conditional statement in front of the condition:

errors = [] for field in required_fields errors.push field_name if field.value == ''

We could even repeat the trick and move everything to a single line:

errors = [] (errors.push field_name if field.value == '') for field in required_fields

Wait. Brackets? The brackets ensure precedence and highlight a point: in CoffeeScript, brackets are optional, not banned. CoffeeScript aims to improve JavaScript’s readability, so if using brackets helps with that, then in they go.

Our validate function now:

window.validate = (form) -> required_field_names = ['name','email'] errors = [] required_fields = (form.elements[name] for name in required_field_names) (errors.push if field.value == '') for field in required_fields false

There are two things left to do – prevent the form from submitting only if we have errors and report those errors to the user.

Bracket bonus: one readable line of CoffeeScript is worth five lines of obscure JavaScript. If using brackets helps with that then in they go

Serving the perfect cup

Preventing submission on error is now trivial. Replace the last line with a check on the number of errors:

window.validate = (form) -> ... errors.length == 0

validate now explicitly provides the yes/no answer to: are there zero errors on the form? This is very readable and maintainable: the final line of the function sums up exactly what the function does. The error reporting could be similarly simple, adding the following before the last line:

alert errors.join(', ') if errors.length > 0

This is too simple, though: no descriptions, just a list of field names. Let’sbreak this out into an error handling function, report. Replace the above with:

report errors

then add the following below validate:

report = (errors) -> alert "This form has errors:\n\n- " + errors.join("\n- ") if errors.length > 0

Our final source code looks like this:

window.validate = (form) -> required_field_names = ['name','email'] required_fields = (form.elements[name] for name in required_field_names) errors = [] (errors.push if field.value == '') for field in required_fields report errors errors.length == 0 report = (errors) -> alert "This form has errors:\n\n- " + errors.join("\n- ") if errors.length > 0

Cleaning up

This sort of quick refactoring has always been a pain in JavaScript. Creating functions can lead to scoping issues, and the syntactical soup prevents refactoring from adding to the readability of the code. With CoffeeScript, pulling out functionality such as the error handling is trivial and does nothing but aid readability. We could continue this to clean up validation further:

window.validate = (form) -> errors = get_errors form, ['name','email'] report errors errors.length == 0 get_errors = (form, field_names) -> errors = [] required_fields = (form.elements[name] for name in field_names) (errors.push if field.value == '') for field in required_fields errors report = (errors) -> alert "This form has errors:\n\n- " + errors.join("\n- ") if errors.length > 0

Thirteen lines of clean, readable code, versus 35 lines of efficient, but pretty much unreadable JavaScript? Now that’s a wake-up call.