- Posted by liammclennan on August 26, 2010
Increasingly I find myself building web applications that rarely, if ever, reload the page. Ajax is used to communicate with the server and to update parts of the page as required. After much trial and error I have come upon a technique for organising the javascript on such pages.
Consider an application that has three buttons: red, yellow and green. When one of the buttons is clicked an rectangle is coloured according to the selection.
The UI design will be as follows:

Here is the demo.
The basic steps in my approach to javascript organisation for such a page are:
- Define a namespace for my custom javascript. In this case ‘TrafficLight’.
- Define an ‘initialize’ method within the top level namespace and call it from jQuery’s document ready event handler.
- Within the initialize method wrap each logical screen component within a javascript object. In this case I will have ‘buttons’ for the buttons and ‘rectangle’ for the rectangle.
- Use a ‘screen activator’ to configure a generic event aggregator so that the screen components do not have to be tightly coupled.
This might seem like overkill for such a simple application but I am trying to demonstrate a technique that considerably reduces complexity for more complicated UIs.
Here is the html for the page. Note the document ready event handler and the call to TrafficLight.initialise().
<html>
<head>
<link rel="Stylesheet" type="text/css"
href="traffic_light.css">
</head>
<body>
<div id="button_container">
<input type="button" value="Red"
class="colour_button"/><br/>
<input type="button" value="Yellow"
class="colour_button"/><br/>
<input type="button" value="Green"
class="colour_button"/>
</div>
<div id="coloured_rectangle">
</div>
<div id="event_aggregator_element"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
</script>
<script type="text/javascript" src="TrafficLight.js">
</script>
<script type="text/javascript">
$(document).ready(function() {
TrafficLight.initialise(
$('#button_container'),
$('#coloured_rectangle'),
$('#event_aggregator_element'));
});
</script>
</body>
</html>
The TrafficLight.js file:
// define the namespace
var TrafficLight = TrafficLight || {};
TrafficLight.initialise = function(
buttonsWrapperSet,
rectangleWrapperSet,
aggregatorWrapperSet) {
// create an event aggregator.
// the array lists the valid events for this screen.
var aggregator = Aggregator.constructor(
aggregatorWrapperSet, ['ColourSelected']);
// create objects for the major screen components
var buttons = TrafficLight.buttonsConstructor(
buttonsWrapperSet, aggregator);
var rectangle = TrafficLight.rectangleConstructor(
rectangleWrapperSet, aggregator);
// create a screen activator to connect the various components
var screenActivator = TrafficLight.screenActivatorConstructor(
buttons, rectangle, aggregator);
screenActivator.activate();
};
TrafficLight.screenActivatorConstructor = function(
buttons,
rectangle,
aggregator) {
var that = {};
that.activate = function() {
// bind the 'ColourSelected' event to the rectangle object
aggregator.bindToColourSelected(rectangle.handleColourChange);
};
return that;
};
TrafficLight.buttonsConstructor = function(buttonsWrapperSet,
aggregator) {
var that = {};
// configure event handlers for the individual buttons
$('input', buttonsWrapperSet).click(function() {
var selectedColour = $(this).val();
// raise the 'ColourSelected' event.
aggregator.triggerColourSelected([selectedColour]);
});
return that;
};
TrafficLight.rectangleConstructor = function(rectangleWrapperSet) {
var that = {};
that.handleColourChange = function(event, new_colour) {
rectangleWrapperSet.css('background-color', new_colour);
};
return that;
};
var Aggregator = {
constructor: function(wrapperSet, supportedEvents) {
if (wrapperSet.length > 1) {
alert("more than one object selected as event aggregator.");
return;
}
var that = {};
$.each(supportedEvents, function(index, eventName) {
that['bindTo' + eventName] = function(callback, data) {
wrapperSet.bind(eventName, data, callback);
Debug.Log('Callback registered for ' + eventName);
};
that['trigger' + eventName] = function(extraParameters) {
wrapperSet.trigger(eventName, extraParameters);
Debug.Log('Triggered ' + eventName);
};
});
return that;
}
};
var Debug = Debug || {};
Debug.Log = function(message) {
if (window.console && window.console.log) {
window.console.log(message);
}
};
- Posted by liammclennan on May 6, 2010
Tonight the Brisbane Alt.NET group is doing a coding dojo. I am hoping to talk someone into pairing with me to solve the kata in CoffeeScript. CoffeeScript is an awesome language, half javascript, half ruby, that compiles to javascript. To assist with tonight’s dojo I wrote the following micro test framework for CoffeeScript:
<html>
<body>
<div>
<h2>Test Results:</h2>
<p class='results' />
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
<script type="text/coffeescript">
# super simple test framework
test: {
write: (s) ->
$('.results').append(s + '<br/>')
assert: (b, message...) ->
test.write(if b then "pass" else "fail: " + message)
tests: []
exec: () ->
for t in test.tests
test.write("<br/><b>$t.name</b>")
t.func()
}
# add some tests
test.tests.push {
name: "First Test"
func: () ->
test.assert(true)
}
test.tests.push {
name: "Another Test"
func: () ->
test.assert(false, "You loose")
}
# run them
test.exec(test.tests)
</script>
<script type="text/javascript" src="coffee-script.js"></script>
</body>
</html>
It’s not the prettiest, but as far as I know it is the only CoffeeScript test framework in existence. Of course, I could just use one of the javascript test frameworks but that would be no fun. To get this example to run you need the coffeescript compiler in the same directory as the page.
- Posted by liammclennan on March 24, 2010
Continuing on my series of builders for C# and Ruby here is the solution in Javascript. This is probably the implementation with which I am least happy. There are several parts that did not seem to fit the language.
This time around I didn’t bother with a testing framework, I just append some values to the page with jQuery. Here is the test code:
var initialiseBuilder = function() {
var builder = builderConstructor();
builder.configure({
'Person': function() { return {name: 'Liam', age: 26}},
'Property': function() { return {street: '127 Creek St', manager: builder.a('Person') }}
});
return builder;
};
var print = function(s) {
$('body').append(s + '<br/>');
};
var build = initialiseBuilder();
// get an object
liam = build.a('Person');
print(liam.name + ' is ' + liam.age);
// get a modified object
liam = build.a('Person', function(person) { person.age = 999; });
print(liam.name + ' is ' + liam.age);
home = build.a('Property');
print(home.street + ' manager: ' + home.manager.name);
and the implementation:
var builderConstructor = function() {
var that = {};
var defaults = {};
that.configure = function(d) {
defaults = d;
};
that.a = function(type, modifier) {
var o = defaults[type]();
if (modifier) {
modifier(o);
}
return o;
};
return that;
};
I still like javascript’s syntax for anonymous methods, defaults[type]() is much clearer than the Ruby equivalent @defaults[klass].call(). You can see the striking similarity between Ruby hashes and javascript objects. I also prefer modifier(o) to the equivalent Ruby, yield o.
- Posted by liammclennan on March 12, 2010
The following code adds a method to javascript arrays that returns a distinct list of values.
Array.prototype.distinct = function() {
var derivedArray = [];
for (var i = 0; i < this.length; i += 1) {
if (!derivedArray.contains(this[i])) {
derivedArray.push(this[i])
}
}
return derivedArray;
};
and to demonstrate:
alert([1,1,1,2,2,22,3,4,5,6,7,5,4].distinct().join(','));
This produces 1,2,22,3,4,5,6,7
Note that this implementation of distinct() is dependant upon my implementation of contains.
- Posted by liammclennan on March 11, 2010
This javascript adds a method to javascript arrays that returns a boolean indicating if the supplied object is an element of the array
Array.prototype.contains = function(item) {
for (var i = 0; i < this.length; i += 1) {
if (this[i] === item) {
return true;
}
}
return false;
};
To test
alert([1,1,1,2,2,22,3,4,5,6,7,5,4].contains(2)); // true
alert([1,1,1,2,2,22,3,4,5,6,7,5,4].contains(99)); // false
- Posted by liammclennan on March 10, 2010
Javascript allows you to declare variables simply by assigning a value to an identify, in the same style as ruby:
Good javascript developers know that this is a bad idea because undeclared variables are assigned to the global object, usually window, making myVar globally visible. So the above code is equivalent to:
window.myVar = "some text";
What I did not realise is that this applies to for loop initialisation as well.
for (i = 0; i < myArray.length; i += 1) {
}
// is equivalent to
for (window.i = 0; window.i < myArray.length; window.i += 1) {
}
Combine this with function calls nested inside of the for loops and you get some very strange behaviour, as the value of i is modified simultaneously by code in different scopes. The moral of the story is to ALWAYS declare javascript variables with the var keyword, even when intialising a for loop. This way i is scoped to the current function.
for (var i = 0; i < myArray.length; i += 1) {
}
- Posted by liammclennan on March 3, 2010
Why Use Client-side Javascript Templates?
When building rich internet applications you often need to construct html on the client. I am going to demonstrate how to construct DOM elements using the jqote jQuery plugin (2.0.0).
The naive approach to client-side html generation is to embed html inside javascript like:
var text = 'Some text';
$('body').append($('<div id="content>' + text + '</div>"'));
This approach fails as the complexity of the html increases. It is also a clear separation of concerns violation to mix html (presentation) with script (interaction).
Javascript templates provide a way to dynamically build html on the client while storing the dynamic html with the rest of the presentation markup. Additionally javascript templates provide MVC-like separation of concerns.
Dynamically Building a Table with a Client-side Javascript Template
Suppose you want a web page that allows the user to build a table of people. For each person the user can enter their name and age. First we need a form to collect the values, and we need some jQuery magic to cancel the form post:
<form id="people_input">
<label for="name">Name: </label><input type="text" name="name" /><br />
<label for="age">Age: </label><input type="text" name="age" /><br />
<input type="submit" value="Add" />
</form>
<script type="text/javascript">
$(document).ready(function () {
$('form#people_input').submit(function (e) {
e.preventDefault();
});
});
</script>
There needs to be a table to display the people, and each time the form is submitted a new row should be added to the table. jqote templates are defined in the html by wrapping the template in a script tag and a CDATA tag. The script tag is necessary to stop the template from being interpreted as part of the page. The CDATA tag is required so that the page remains syntactically valid. Here is my empty table and a template for a row:
<table id="person_table">
<tr>
<th>Name</th><th>Age</th>
</tr>
</table>
<script type="text/html" id="template">
<![CDATA[
<tr>
<td><$= this.name $></td><td><$= this.age $></td>
</tr>
]]>
</script>
The default jqote tags use <%= %> syntax which I have replaced with <$= $> to avoid conflict with Asp.Net MVC. Inside of a jqote template ‘this’ refers to the data object used to render the template. Within the jqote tags any javascript is valid since jqote is based on John Resig’s Javascript Micro-Templating.
The final, and most interesting step is to render the template each time the form is submitted and insert the result at the end of the table. Note the second parameter to the jqote() method, which is the character to use for the html nugget syntax.
$(document).ready(function () {
$('form#people_input').submit(function (e) {
var data = { name: $('input[name=name]').val(), age: $('input[name=age]').val() };
$('table#person_table').append($('#template').jqote(data, '$'));
e.preventDefault();
});
});
To try out the complete code, grab it from codepaste.net.
- Posted by liammclennan on November 27, 2009
Javascript does not have classes in the traditional sense, but we can achieve something similar in a number of ways. C# and Ruby both have standard class syntax.
This post is part of a series comparing the language features of the C#, Javascript and Ruby programming languages.
C#
public class Vehicle
{
protected string Make { get; private set; }
protected string Model { get; private set; }
public Vehicle(string make, string model)
{
Make = make;
Model = model;
}
public virtual void Print()
{
Console.WriteLine(GetDescription());
}
protected string GetDescription()
{
// string formatting syntax return string.Format("Make: {0} Model: {1}", Make, Model);
}
}
public class Helicopter : Vehicle // inheritance syntax {
public int NumberOfRotorBlades { get; private set; }
public Helicopter(int numberOfRotorBlades, string make, string model)
: base(make, model)
{
NumberOfRotorBlades = numberOfRotorBlades;
}
public override void Print()
{
// string concatenation syntax Console.WriteLine(GetDescription() + " Number of Rotor Blades:" + NumberOfRotorBlades);
}
}
Javascript
Class:
// declare vehicle 'class' var vehicle = function(seed) {
var that = {};
// private method var getDescription = function() {
return "Make: " + seed.make + " Model: " + seed.model;
};
// public method that.print = function() {
alert(getDescription());
};
return that;
};
// instantiate a vehicle var magna = vehicle({
make: 'Mitsubishi',
model: 'Magna' }); magna.print();
Derived class:
// declare helicopter 'class' var helicopter = function(seed) {
var that = {};
that.prototype = vehicle(seed);
var getDescription = function() {
return "Make: " + seed.make + " Model: " + seed.model;
};
that.print = function() {
alert(getDescription() + " Number of Rotor Blades:" + seed.numberOfRotorBlades);
};
return that;
};
// instantiate a helicopter var ah64 = helicopter({
make: 'Hughes Helicopters',
model: 'AH-64',
numberOfRotorBlades: 4 }); ah64.print();
Ruby
class Vehicle def initialize(make, model) @make = make;
@model = model;
end
def print
puts get_description
end
private
def get_description
return "Make: #{@make} Model: #{@model}" end end magna = Vehicle.new('Mitsubishi', 'Magna')
magna.print
class Helicopter < Vehicle
def initialize(make, model, number_of_rotors)
super(make, model)
@number_of_rotors = number_of_rotors
end
def print
puts get_description + " Number of Rotors: #{@number_of_rotors}" end end ah64 = Helicopter.new("Hughes Helicopters", "AH-64", 4)
ah64.print
- Posted by liammclennan on November 24, 2009
This post is part of a series comparing the language features of the C#, Javascript and Ruby programming languages.
Variables
C# requires that variables be declared with a specific type. Javascript and Ruby determine the type of variables at runtime. Here is the syntax:
C#
public string publicMessage = "Hello World";
private string privateMessage = "Hello World";
static string PRIVATE_MESSAGE = "Hello World";
Javascript
var message = "Hello World";
Javascript does not have a syntax for making variables public or private, instead it is achieved by a clever usage of closure (discussed later). Javascript does not have block scope, which means that variables defined within a block can be accessed from outside the block (a block is anything in {} such as and if statement or a loop). In Javascript variables are scoped to the function in which they are declared and are visible anywhere within that function, including within inner functions.
// this function will alert "Hello World" twice.
function showMessage() {
if (true) {
var message = "Hello World";
}
alert(message);
var innerFunction = function() {
alert(message);
};
innerFunction();
}
Ruby
A ruby variable is declared by assigning to the variable name. Ruby uses prefixes to indicate the variable scope.
# local variable
message = "Hello World"
# instance variable
@message = "Hello World"
# static variable
@@message = "Hello World"
# global variable
$message = "Hello World"
- Posted by liammclennan on November 21, 2009
Sometimes you have a t-shirt idea, but there is no way to try it out – until now. Introducing OnAShirt.net. It is a simple app I coded in a couple of hours today that allows the user to place text over a picture of a t-shirt, or even to conduct t-shirt conversations with themself.
Thanks to jQuery for making this sort of thing so easy.