The Tale of the Flink

(A wee intro to event handling in Javascript)

This is my attempt to write a THE SIMPLEST EXAMPLE OF AN EVENT HANDLER EVAR.

Or, alternatively, how to make your web pages do javascript tricks, impress your friends, and influence people.

What, pray tell, is an event?

In the waaay, early days of HTML, as in, on the first web page, there was really only one event. In the beginning, there was the click. And there still is the click. The click was what made some underlined text, well, do something, when you did something. When you clicked it.

That, my friend, is an event. When you do something.

To something.

And then something happens.

And then what you did makes something happen.

Let's review.

the element
Something in the document, like a link.
the event
The user does something, like click.
what happens
The browser does something. This is either built-in behavior, or some Javascript function that you can write.

The trick is to learn how to get those three things to work together.

What's in a Link

I think an interesting way to look at how events work is to "implement" this fundamental event: the link. So, we'll make a span that behaves and looks (almost) just like a link.

So, let's recreate the humble link.

We'll call our little imposter a flink, a fake link.

Note that this is utterly pointless in practical terms. We're just expermenting.

In the HTML we code our flink thusly:

→ the element

<span id="flink">Click me I'm a flink!</span>

It's just a span.

Here's some CSS:

#flink {
    color: blue;
    cursor: pointer;
    text-decoration: underline;
}

This will make some text look like a link. The cursor line will even make the cursor turn into a little hand, just like a real link! OMG!

→ what happens

Let's skip straight to the "what happens" bit of of our three-part flink creation:

function goToYahoo(){
    document.location = 'http://yahoo.com/';
}

That's what we'll have the flink do. Go to Yahoo. Exciting. Now the part the user does:

→ the event

'click'

No really, that's it: a span, like just about every element in a document, can be told to listen for a click.

Okay, let's pause here and look at our page. It's not going to do anything yet, because we haven't hooked up the three parts. Be sure to look at the source, simple though it may be.

Remember, it won't work (yet!).

flink page, version 1

And take a look at the Javascript, too. You can see how we get references to the elements, even though they're not doing anything yet.

flink javascript, version 1

Browser Woes

Right here is where I insert the gruesome details about the differences between browsers, and scary stuff about IE memory links and variations in doing event handling and yadda yadda.

Have fun imagining that part.

Fortunately, we get to ignore all that, because other, smart people have written utility functions that cut to the chase. We just have to learn to use their tying-together utility function. Thank you, other, smart people.

Tying it All Together

So we're ready to hook all this up: "turn on" the element so that our function runs when the event occurs -- in our case, the user clicks the flink, and then the browser goes (in this case, to yahoo.com).

(Unfortunately, there's a second step after we get that far, but it's not too hard so hang tight.)

When the click event fires the function, we say that the event is "handled" by the function, so you'll see a lot of tutorials referring to all this as "event handling."

Now, here's the aforementioned utility function courtesy of Dean Edwards. Just pretend that his events.js is a function that was unfortunately left out of core Javascript. It's your friend, because it does exactly what I've just described: it ties together an element, an event, and a function.

Here's an example of how to use it:


// get a reference to the element in the HTML
var flinkSpanRef = document.getElementById('flink');
// connect it up so it runs our function when clicked 
addEvent(flinkSpanRef, 'click', goToYahoo)
And here's a more generalized look. Remember, these are the three elements:
theElement
You get a reference to this with document.getElementById('someIdInYourHtml').
theEvent
In our case, we want to list for a 'click' -- but it could be a 'mouseover', 'keyup', etc. etc.
theFunction
This is a function, because functions happen! Our function is goToYahoo.

addEvent(theElement, 'theEvent', theFunction)

Note that only theEvent is a string, the others aren't, so they're unquoted.

flink page, version 2

MAKE IT WORK RIGHT NOW, DAGNABBIT

Okay, here's the WORK RIGHT NOW DAGGNABBIT version:

If you put all that in a 'script' tag at the bottom of your page, after the #flink element has already loaded into memory, it will work. Like this:

flink page, version 3

But the whole point of this exercise is to create a flink which is smoothed out on the unobtrusive tip. Which means, no inline Javascript.

MAKE IT WORK RIGHT NOW, DAGNABBIT, BUT NOT BE SUCKY

We just have to explain how to light the candle. The candle is the conjunction of the element, event, and function that we've been discussing, and lighting is is telling that tied up contraption to do its thing.

To understand this next bit, I need to explain the load event to you. It's kind of weird.

In the DOM, the fact that the page has finished loading is treated as an event. This autonomous event is the 'load' event, and it belongs to the window object. That is to say, the whole page.

(If I'm not mistaken, the window's load event is the only event which doesn't involve the user doing anything with the keyboard or mouse. None of the resources that I've read point this fact out, but it really helped me to understand all this stuff once it had sunk in.)

The upshot:

When you run the addEvent function to tie together an element, an event, and a function, you must in turn wrap that in an initialization function that runs when the window has finished loading.

It sounds scary but you only have ot learn how it works once. Check out the code here:

It seems to me that this should be the default behavior. Oh well. We just have to use a utility function to make it happen.

flink page, version 4

Here's what the wrapper function looks like:


// this is the wrapper
function init(){
    // your addEvent calls here 
}

// run init() when the page has loaded 
// (light the candle!)
addEvent(window, 'load', init);

So all that's left is to stick the addEvent call we wrote before inside the init:



// this is the wrapper
function init(){
    // we've done this before!
    var flinkSpanRef = document.getElementById('flink');
    addEvent(flinkSpanRef, 'click', goToYahoo)
}

// run init() when the page has loaded 
// (light the candle!)
addEvent(window, 'load', init);

We have achieved Nirvana: there's no Javascript code in the HTML at all, just a couple of calls to external scripts.

Doing other stuff

Now that you've seen how all this addEvent jazz is put to work, here's another page with some other events you can play with. You'll notice that you can do more than one "tying together" sort of addEvent call into the init-- you only have to init once.

a page with a flink and some other nutty stuff.

There's a bunch more new stuff in there in terms of what events are in use and how exactly you go about messing with CSS and so on, but you should be able to get a general idea of how event handling is working.

Further resources

I've been reading quite a bit about event handling in Javascript. Here are some key articles:

PPK's introduction to events
This guy's site is the place for Javascript.
The original addEvent function (I think)
Had some problems that more recent versions have fixed.
Simon Willison's intro
An influential post about dealing with the 'load' event.
Unobtrusive Javascript
This is probably the best overall introduction to the ideas behind getting Javascript out of your HTML.
DHTML Utopia: Modern Web Design Using JavaScript & DOM
Most of this stuff is covered in Stuart Langridge's new book, my copy of which is already well-stained with coffee.
jQuery: New Wave Javascript
jQuery is way cool, and will I think eventually make most of this stuff obsolete.