A quick Javascript formatting tip 10
IE’s a pain. The particular pain I want to write about is its pickiness about Javascript object literals. Consider the following Javascript object:
{ success: function () {...},
failure: function () {...},
}If you’re used to programming in Perl or Ruby, that trailing comma’s perfectly fine, in fact leaving it there is sometimes considered good practice because it makes it easy to extend the hash, just add a new row and leave another trailing comma.
The trouble is, it’s not strictly legal to do that in Javascript. Pretty much every implementation out there will allow it though.
Except IE.
So, I’ve taken a leaf out of Damian Conways Perl Best Practices and writing my object literals as:
{ success: function () {...}
, failure: function () {...}
}By sticking the comma at the beginning of the line, I’m never going to make an object that breaks IE, and adding a new line to the hash is straightforward too. Just stick the cursor in front of the }, type my leading comma, space, attribute name, and hit return when I’m finished.
I’ve also started using the same practice pretty much everywhere else that I’ve got a comma separated list of things:
var foo
, bar
, baz
;
$.each( anArray
, function () { ... }
);It looks weird at first, but trust me, it grows on you.
Update
In the comments, I make reference to tweaking Steve Yegge’s excellent js2-mode to handle leading comma style a little more gracefully. Since then, I’ve made it work and attached a diff to this issue on the project’s issue tracker.
Javascript scoping makes my head hurt 13
Who came up with the javascript scoping rules? What were they smoking. Here’s some Noddy Perl that demonstrates what I’m on about:
my @subs;
for my $i (0..4) {
push @subs, sub { $i }
}
print $subs[0]->(); # => 0;Here’s the equivalent (or what I thought should be the equivalent) in Javscript:
var subs = [];
for (var i in [0,1,2,3,4]) {
subs[i] = function () {
return i;
}
}
alert subs[0]() // => 4What’s going on? In Perl, $i is scoped to the for block. Essentially, each time through the loop, a new variable is created, so the generated closures all refer to different $is. In Javascript, i is scoped to the for loop’s containing function. Each of the generated closures refer to the same i. Which means that, to get the same effect as the perl code, you must write:
var subs = [];
for (var shared_i in [0,1,2,3,4]) {
(function (i) {
subs[i] = function () {
return i;
};
})(shared_i);
}
subs[0]() // => 0Dodgy Ruby scoping
I had initially planned to write the example “How it should work” code in Ruby, but it turns out that Ruby’s for has the same problem:
subs = [];
for i in 0..4
subs << lambda { i }
end
subs[0].call # => 4Which is one reason why sensible Ruby programmers don’t use for. If I were writing the snippet in ‘real’ Ruby, I’d write:
subs = (0..4).collect { |i|
lambda { i }
}
subs[0].call # => 0My conclusion
Javascript is weird. Okay, so you already know this. In so many ways it’s a lovely language, but it does have some annoyingly odd corners.
Getting to grips with Javascript
I’ve been busily adding AJAX features to the work website, and I got bored of writing Form handlers. I got especially bored of attaching similar form handlers to lots of different forms on a page, so I came up with something I could attach to document.body and then plug in handlers for different form types as I wrote them.
So, I wrote FormSender and set up my event handler like so:
FormSender.onSubmit = function (e) {
if (canDispatch(e)) {
YAHOO.util.Event.stopEvent(e);
YAHOO.util.Connect.setForm(e.target);
YAHOO.util.initHeader('Accept', 'application/javascript, application/xml');
YAHOO.util.Connect(e.target.method.toUpperCase(), e.target.action,
callbackFor(e));
}
};
jQuery(document.body).each(function () {
YAHOO.util.Event.addListener(this, "submit", FormSender.onSubmit);
});jQuery(e.target).hasClass('ajax'), but there was a snag. We had two sorts of forms on our pages, forms built using form_for(..., :class => 'ajax') and forms built using button_to(..., :class => 'ajax'), and they attached their classes in different places. In the form_for case, the class was on the form tag, but in the button_to case, it was on the generated form’s submit field. One option would be to monkey patch button_to, or roll my own ajax_button_to, but I ended up writing canDispatch like so:
function canDispatch(e) {
jQuery(e.target).find(':submit').andSelf().hasClass('ajax');
}This uses jQuery to build a list of the form, and its submit button, and then checks to see if any member of that list has the class ‘ajax’.
So, we can now tell if the source of a submit event is a form we should be doing AJAX dispatch with. The next trick is to work out what needs to be done with the results of sending the form. One option is the Prototype trick of simply evaluating the returned javascript, but it often makes sense to keep the behaviour clientside and just have the server return a datastructure. I decided that the way to do this would be by adding a second class to a form which used a none default handler, and then keep a hash of callback constructors keyed by class. This made callbackFor look like:
function callbackFor(e) {
var candidates = candidateClasses(e.target);
for (var i = 0; i < candidates.length; i++) {
if (FormSender.callbacks[candidates[i]]) {
return new FormSender.callbacks[candidates[i]](e);
}
}
return new FormSender.callbacks.ajax(e);
}candidateClasses is, again, a little more complex than I’d like, by virtue of the differences between button_to and form_for differences, but still reasonably straightforward, thanks to jQuery:
function candidateClasses(element) {
return
jQuery(element).find(':submit').andSelf()
.filter('.ajax').attr('className')
.replace(/ajax/, '').trim().split(/ +/);
}JQuery gets the form and its submit button, then selects the tag that has the ‘ajax’ class and pulls out the full className string. The replace gets rid of ‘ajax’, trim chops any useless whitespace off either end, and split(/ +/) turns it into an array of classnames. The replace -> trim -> split pipeline has the feel of something that must already exist in some DOM interface somewhere, but I’m not sure where, so I rolled my own.
Once we have a list of classes it’s easy to just cycle through the candidates until we find one that matches a callback constructor, falling back to the default where nothing matches.
For completeness, I’ll show you my current default handler, which I expect to be extending to deal with a couple more media types and, in the case of the failure handler, more failure statuses.
FormSender.callbacks.ajax = function (e) {
var form = e.target;
this.scope = form;
};
FormSender.callbacks.ajax.prototype.success = function (o) {
switch (o.getResponseHeader['Content-Type'].replace(/;.*/, '')) {
case 'application/javascript':
case 'application/x-javascript':
case 'text/javascript':
eval(o.responseText);
break;
default:
YAHOO.log("Can't handle AJAX response of type " + o.getResponseHeader['Content-Type']);
}
};
FormSender.callbacks.ajax.prototype.failure = function (o) {
switch (o.status) {
case 401:
Authenticator.loginThenSubmit(this);
break;
default:
switch (o.getResponseHeader['Content-Type'].replace(/;.*/, '')) {
case 'application/javascript':
case 'application/x-javascript':
case 'text/javascript':
eval(o.responseText);
break;
default:
YAHOO.log("Can't handle AJAX failure response of type " + o.getResponseHeader['Content-Type']);
}
}
};You’ll notice a reference to Authenticator.loginThenSubmit in the 401 handler, but that’s something I’ll save for another day.
A note on namespacing
Although I’ve been showing the various FormSender helper functions as if they were in the global namespace, in the real code they’re wrapped in a function call:
var FormSender = (function () {
var candidateClasses = function (element) {...};
var callbackFor = function (e) {...};
...
var onSubmit = function (e) {...};
return {onSubmit: onSubmit, callbacks: {}};
})();I love the (function () {...})() pattern – it’s a great way of keeping your paws out of the global namespace until you really, really need to.
FormSender Benefits
Aside from the obvious benefit of drastically reducing the number of onSubmit event handlers registered with the browser, I found that using FormSender has simplified some of my response handlers. For instance, one form would get a chunk of html back from the server and would use that to replace the div that contained the form. But the new div also contained a form that needed to have Ajax behaviour, so a chunk of the handler code was concerned with reregistering onSubmit handlers for the new form (or forms). No fun. By switching to a single, body level, form handler, that problem simply disappears – so long as the new forms have the right class, they automatically get the appropriate behaviour. Result.
Obviously, FormSender is unobtrusive javascript, which is nice, and its pluggable nature means it’s easy to extend just by writing new response handlers and registering them with the FormSender object.
Future Directions
One obvious extension to FormSender is to pull out the meat of the onSubmit method into the callback object to allow for forms that don’t simply send themselves to the server. Another is to wrap my head around the workings of Javascript’s object model to make it easy to build handlers that don’t duplicate the behaviour of the default handler through the medium of copy and pasting code…
Your comments please?
I’m still very new to Javascript as a programming language and I’m sure I’m doing plenty of boneheaded things here. Please let me know if there’s things I can do to improve this, or point me at any libraries that already cover this ground.
So... I was wondering 3
Has anyone written an Atompub client in JavaScript yet?
Finally learning JavaScript 10
For years I’ve managed to dodge learning JavaScript. People have told me it’s a fine language with some dodge implementations. Friends have built an entire business model in the language, and I’ve continued to treat as if it were The Sound of Music (I’m 39 now and I have managed to completely avoid watching that film).
Except, today I finally had a technical need that I couldn’t dodge by writing another .rjs file, so with the help of the Prototype docs, a helpful article or two and healthy dose of cribbing from others, I was off.
What do you know, it really is a fine language. I doubt I’m writing idiomatic code though – I’m leaning heavily on closures and private member functions, and I haven’t written a single for loop yet, but it seems to accommodate my style.
Now, if I can just get my head round the innards of keyboard events I shall be a great deal happier.
