Modern JavaScript – a primer…

Soon I’m meeting with a couple of fellow web geeks and they’ve asked me to provide a quick talk about modern JavaScript including that whole pesky AJAX thing. In return they’ll be giving me some help with the .net thing. Anyway, I thought I’d better write something down so I don’t look like a complete muppet, so here’s what I intend to say.

The old way – onclick and javascript: hell

Let’s face it, separation is a good thing. And just as all good web people use HTML to describe the page content, and a separate CSS file(s) to style that content, so a separate JavaScript file(s) to describe the behaviour of the page should be used. That means no more ‘onclick=’ attributes or ‘a href=”javascript:”‘ psuedo-links. Those things were OK for a time, but people who know better now use different methods to apply certain JavaScript behaviours to elements using element type names, IDs and classes.

It’s better for lots of reasons: reusability of code, readibility of HTML, compatibility with a wider array of devices, accessibility to a wider range of users, separateion of content and behaviour, and quite a few more. It’s simply the Right Thing To Do.

The new way – listen up!

So how do we make that funky JavaScript function happen when someone clicks the link? An example would be useful. Let’s say we want to change an image when someone clicks a link, like in a simple image gallery. Here’s our link and image HTML:

View image 2

Image 1

And here it is, in all it’s glory. What we want to do is listen for when a user clicks the link, then hijack the request and load the right image in using JavaScript. Eh? Listen? What are you going on about?

Listeners are the unobtrusive way to assign behaviours to events performed on elements. An element is any HTML element – a link, a button, an image, the page body itself etc. An event is something that can happen to an element – a link can be clicked, an button pressed, a the page body loaded. And a behaviour is something that happens when that event is fired – a popup window appearing, changing the text size, checking the validity of a forms fields.

It’s pretty simple, really. Rather than shove a link to the JavaScript function in the actual link or button (as described above) we simply set a load of listeners that sit there patiently until something happens. They then jump up and say “Hey! Someone clicked that link! Now I have to do something!”. That’s separation of behaviour and content in action.

So how do we actually set listeners? Here’s a simple one that listens for our link with the id ‘image2link’ being clicked:

// the addEvent and removeEvent functions are from http://ejohn.org/projects/flexible-javascript-events/

// This function adds a listener for a particular event
// (from http://ejohn.org/projects/flexible-javascript-events/)
function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj.detachEvent( 'on'+type, obj[type+fn] );
obj[type+fn] = null;
} else
obj.removeEventListener( type, fn, false );
}

// And here's where it happens

// this line adds a listener to listen for then the page loads and then runs the getImageLink function
addEvent(window,'load',getImageLink,false);

// Here's where we get the image link from it's ID
function getImageLink(){
// This line gets a reference to the element with the ID 'image2link'
var thisLink = document.getElementById('image2link');
// this line adds the listener to listen for then that element is clicked, then runs the 'showImage' function
addEvent(thisLink,'click',showImage,false);
thisLink.onclick = cancelClick;
}

// this function shows the image
function showImage() {
// get a reference to the element with the ID 'image'
var imagePlaceholder = document.getElementById('image');
// set the new src attribute
imagePlaceholder.setAttribute('src','image_2.jpg');
// set the new alt attribute
imagePlaceholder.setAttribute('alt','Image 2');
// stop anything else from happening
cancelClick();
}

// This function stops the default click from happening
function cancelClick() {
return false;
}

Make sense? That’s pretty much it, to be honest. I’ve added this JavaScript into our simple page and you can see the results here. Clicky linky! I love it long time. The problem is it’s long and complicated and way beyond the means of any but the most geeky of coders. This is where frameworks come in, which I’ll talk about in a little while.

Graceful degradation and progressive enhancement

Using this method you can set up as many functions as you like, and set listeners to any event on any element to start them. And perhaps the best bit about this is that because you’re not messing about with the actual href of the link at all, it can point to the actual image you want to load so that if a user doesn’t support JavaScript (or has it disabled) they can still follow the link as normal and see the image.

That is known as graceful degradation, or progressive enhancement. What it means in this example is that if a user can’t or won’t support JavaScript they at least get something. It’s another way for websites to be accessible and, again, is simply the Right Thing To Do.

Manipulating the DOM

In that example we changed the src and alt attributes of an image element. That’s an example of manipulating the DOM. The DOM, or Document Object Model, is the current rendering of a web page – the treeview of nested element, if you like. For example, the html element has a body element inside it. The body element has li and p elements inside it. We can walk through the DOM just like we’re walking through an XML document. In fact, a properly-written XHTML is an XML document.

So what else can we do with JavaScript to change our web page? Here’s a partial list of functions that are part of the core JavaScript language. Try to guess what they do:

  • getElementById
  • getElementsByName
  • getElementsByTagName
  • createAttribute
  • setAttribute
  • removeAttribute
  • createTextNode
  • appendChild
  • removeChild
  • cloneNode

A more complete list can be seen at JavaScriptKit.com.

Now, this is where it gets a bit complicated, and where opinion on modern JavaScript best practices hits a fork in the road. I’ll give you an example. Let’s say that we have a list of options, like this:

  • Item 1
  • Item 2
  • Item 3

And here it is, looking lovely. We want to add another item to the bottom of the list, cleverly called ‘Item 4’. One way to do this could be:

// create the new list item
var newitem = document.createElement('li');

// set the text for this new list item
var newtext = document.createTextNode("Item 4")

// add the new text to the new list item
newitem.appendChild(newtext);

// get a reference to the list and add the new item with it's text to the list
// this line demonstrates the ability to string together object methods and properties
// this method can save a lot of typing and bandwidth
document.getElementById('list').appendChild(newitem);

That should all make sense, and while it is perfect from a code point of view (well, notwithstanding my fumbling fingers ;0) If you want to see this in action you can’t do better thank to click here. This page adds a new item to the list 3 seconds after the page is loaded. I won’t pretend it’s quick and easy. Can you imagine adding a complex block of HTML to a page this way? Nasty, I think you’ll agree. But that’s the way the DOM cookie has crumbled, and we’re stuck with it.

Or are we? There is another non-standard method which is helpfully supported by all the major browsers and is used extensively by thousands of modern sites. It’s called ‘innerHTML’ and I bet you can guess what it does from the name. That’s right, it can get and set the entire HTML code inside a referenced element. Here’s an example:

// here's the HTML

This is my text!

// get a reference to our paragraph and get the innerHTML property
var text = document.getElementById('innertest').innerHTML;

// the variable 'text' now contains 'This is my text!'

// set some new text
document.getElementById('innertest').innerHTML = "No! It's my text!";

// the HTML will now be

No! It's my text!

Now, is that easier or what? You can see it working here. The innerHTML property can contain any HTML you want – links, images, tables, forms, the lot. So it’s a really easy way to get and set large amounts of HTML. And there’s even some evidence to say it’s faster than a totally DOM-only method for writing HTML.

Of course, not everyone agrees that this is a good method, but it works, is well supported, and is likely to be included in the official DOM specification eventually.

AJAX

So now we can change huge chunks of a web page using JavaScript easily, surely there’s some really cool way we could use this? Why, yes there is, and it’s called AJAX. That stands for Asynchronous JavaScript and XML and was coined by the clever chaps at Adaptive Path to describe a collection of technologies working together to do a simple thing: send messages to and from the web server without refreshing the page.

It might not sound much, but that technology (which was originally developed by Microsoft several years ago) is the breakthrough that web developers needed to get on an (almost) level footing with desktop application developers. For too many years we’ve provided cool online apps that look great, do great stuff, but every time we wanted to ‘do’ something – change a password, request a different ordering for a data table, send a form – we’ve had to sit back and wait for the whole damn page to be sent, and then another whole damn page to be returned. Only to tell us that ‘Sorry, your password is not valid. It must contain letters, numbers and a Klingon war-cry’.

No more! We’re even. We’ve got it all: the looks, the worldwide reach, the sexiness, the coolness and now the responsiveness. AJAX has made this possible, and it’s all down to a little set of functions like this:

// these are my AJAX functions from my projectGenie application

// function to send some data and receive the response
function sendReceive(divID,scriptPath,variables,divClass)
{
if (document.getElementById){
var thedate = new Date;
var datestamp = escape(thedate.getTime());
url = scriptPath + '?datestamp=' + datestamp + '&' + variables + '';
if (window.XMLHttpRequest)
{
request = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
request = new ActiveXObject("Microsoft.XMLHTTP");
}
request.onreadystatechange = processReqChange;
request.open("GET", url, false);
request.send(null);
contentText = request.responseText;
updateDiv(divID,contentText,divClass);
}
}

// function to process the request status change
function processReqChange()
{
if (request.readyState == 4){
if (request.status == 200){
} else {
if (request.status == 404) {
alert("There was a problem retrieving the data, the page was not found.");
} else if (request.status == 500) {
alert("There was a problem retrieving the data, there was a server error.");
} else {
alert("There was a general problem and the data could not be loaded.");
}
}
}
}

// function to update an element (notice the innerhtml?)
function updateDiv(divID,textContent,divClass)
{
if (document.getElementById){
var element = document.getElementById(divID);
element.innerHTML = textContent;
element.className = divClass;
addListeners();
}
}

There’s not really much there, so I won’t explain it. Oh, OK then, I will :0)

// function to send some data and receive the response
// this takes the following parameters:
// divID = the element that should receive the response
// scriptPath = the path (relative or absolute) of the server-side processing page
// variables = the variables to send to the processing page
// divClass = the class to set the receiving element when the response has been received
// (this is useful for unhiding things, highlighting things etc)
function sendReceive(divID,scriptPath,variables,divClass)
{
// check if the browser supports the document.getElementById method
// if not the function is not run
if (document.getElementById){
// set a new datestamp, this ensures we don't see cached versions of the processing page
var thedate = new Date;
var datestamp = escape(thedate.getTime());
// set up the URL we will be sending to and receiving from
url = scriptPath + '?datestamp=' + datestamp + '&' + variables + '';
// check if the browser supports the window.XMLHttpRequest method
// if so use this method
if (window.XMLHttpRequest)
{
// create the new request
request = new XMLHttpRequest();
}
// otherwise check if the browser supports the window.ActiveXObject method
// if so use this method as we're in IE
else if (window.ActiveXObject)
{
request = new ActiveXObject("Microsoft.XMLHTTP");
}
// set the function to run whenever the state of the processing page changes
request.onreadystatechange = processReqChange;
// open the connection to the processing page using GET
// the 'false' means this is asynchronous, so the browser doesn't lock up while we process the data
request.open("GET", url, false);
// send the data - this is null because all the variables we are sending are by GET (querystring)
request.send(null);
// get the response of the processing page
contentText = request.responseText;
// run the updateDiv function to write the response to the page
updateDiv(divID,contentText,divClass);
}
}

// function to process the request status change
function processReqChange()
{
// these are all pretty obvious, as they are standard HTTP status codes
if (request.readyState == 4){
if (request.status == 200){
} else {
if (request.status == 404) {
alert("There was a problem retrieving the data, the page was not found.");
} else if (request.status == 500) {
alert("There was a problem retrieving the data, there was a server error.");
} else {
alert("There was a general problem and the data could not be loaded.");
}
}
}
}

// function to update an element (notice the innerhtml?)
function updateDiv(divID,textContent,divClass)
{
// check if the browser supports the document.getElementById method
// if not the function is not run
if (document.getElementById){
// get a reference to the element
var element = document.getElementById(divID);
// set the innerHTML of the element to be the textContent
element.innerHTML = textContent;
// change the class name of the element (this could be done as element.setAttribute('class', divClass);)
element.className = divClass;
// now we reset the listeners so we can catch the next click
startListeners();
}
}

// and to kick all this off we do this:
sendReceive('result','add.php','a=1&b=2','success');

// of course, this line could be included in a function that has a listener attached to it

The ‘add.php’ file Would then take the values for the variables ‘a’ and ‘b’ and add them together (giving us 3 in total, in case you didn’t know) and then send the result back to the requesting page to be displayed in the ‘result’ element. While that example is pretty simple, there really is everything in there to build advanced web applications with the responsiveness of desktop apps. Depending on connection speed, responsiveness of your web server, of course. There is loads more help on this on the web, a lot of it very very complicated, but it all boils down to that principle.

There’s a really simple example here that just loads a line of text from an HTML file.

Object-oriented JavaScript

I want to touch on object-oriented JavaScript, as some of you might not believe that JavaScript is a powerful object-oriented language. Well it is, in fact everything in JavaScript is an object – functions, the lot. Perhaps the best way to demonstrate this is to use an example from The Wild.

JavaScript frameworks – Prototype

A framework, as you may know, is ‘defined support structure in which another software project can be organized and developed‘ – basically a way to stop you from having to reinvent the wheel every time you create some code. The Prototype JavaScript framework provides an easy way to use all of the functions I’ve touched on above – and a huge boatload more – in just a few simple lines of code.

I can’t possibly do justice to Prototype’s fantastic feature set, for that you need to consult the Prototype documentation. All you need to know about AJAX with Prototype is fantastically demonstrated at 24ways.org, in an article written by Drew McLennan. Is that easy or what?

An example – the ODE application

A while ago I wrote a little PHP/JavaScript application called ODE – Online Development Environment. It does just what it says on the tin, allowing you to browse a folder structure on a web server, load multiple text files, edit and save them. It has quite a few nice features such as showing an error if you’re about to close a file that you’ve edited without saving, and runs completely from one HTML (well, PHP) file with no page refreshes.

The danger of being driven…

Something I read on Andy Budds website the other day got me thinking. In an entry about the end of the web standards awards, an unofficial celebration of the power and beauty of web standards-compliant sites, he says the following:

What I wanted to do was set up some kind of CSS showcase, highlighting the best designs around. Not experimental designs such as those found in the Zen Garden, but real world designs being used in the wild. However time is always the enemy of invention, and I never managed to get my idea off the ground. This is why I was excited when a young designer from Sweden sent me a link to a project he was working on called the Web Standards Awards. The design was super-sexy, and the fact that something was already in the works meant that I didn’t have to do it myself.

I’ve added the boldification to highlight the bit that particularly struck me. I may well be reading too much into this, but I think this little comment hints at something that is indicative of many web people, and it can be a big problem. It’s certainly something that I struggle with much more than I should.

And it’s all to do with ideas. You know the story: you have an idea for a new website (or business, or painting, or song, or whatever) and for a time it consumes you. It’s one of those ideas that makes you smile, and sets your mind galloping off with wild abandon through the lush, dense, scented jungle of detail. This is a great idea, maybe even The Idea™, and you feel good inside knowing that you have this precious gift.

But that’s not enough, you need to turn the idea into reality. So you start coding (or start writing plans and projections, or get your brushes out, or tune up your guitar or whatever) and everything is flowing. You’re in the zone, white-hot, cooking on gas.

And maybe you finish turning the idea into reality, maybe you don’t. Maybe it’s an idea that revolutionises the planet, maybe it isn’t. That’s not important to our discussion, because by now you’ve realised that the intial motivation is what has got us this far.

And that’s what I really want to talk about: motivation. In this example Andy was glad that someone else had done the site, not because he wasn’t motivated to do it (in fact he’d already been keeping his own list, but because motivation has to be effective. In essence, motivation isn’t real until it is translated into action. And that’s a problem, because web people are known for their numerous pet projects.

Now the strangest thing about this, and this is certainly something that I know to be true for myself, is the motivation that drives us to do these pet projects seems entirely noble. We’re changing the world, providing a service that people (may) need, helping others, sharing knowledge etc. That feeling is like a drug, and can very easily get out of hand. As Andy hinted, he felt he had to do a CSS showcase site. There was no choice, it was a great idea and somebody had to do it.

You see, Andy could have wanted to do the Web Standards Awards from purely selfish reasons – to make money, make him more powerful, or make him more famous. Or probably all three. But I don’t believe he did. I think he was motivated to do good, to share his knowledge, to celebrate web standards with like-minded people. And maybe make some new friends, gain a bit of recognition and bolster his portfolio along the way.

The problem for us web people is that we get so many ideas, or at least I do. If it’s not one thing it’s another (and that’s without mentioning the other projects I’ve got bubbling on the digital hob at the moment). How do we manage?

Well, the answer for me is that I don’t. I spend far too much time looking at a screen, never feeling that I’ve made very much progress, certainly never getting anything finished, and never feeling completely happy with anything I’ve done. Yes, I could just pack it in and become a park ranger, or I could learn to handle the motivations and ideas that bombard me daily. My ideas book – guess what, it’s a book where I write down my ideas – helps a lot. Maybe one day I’ll have enough time to do this stuff, or maybe by then I’ll have realised they don’t matter that much after all.

So, your idea may be fantastic, you may really enjoy bringing it to life, but don’t fall into the trap of chasing after the latest new-cool fix at the expense of your life, family, day-to-day work and those things that really matter. In short, we have to learn to let things go.