JavaScript is a functional language. When you program it like that, you get the greatest enjoyment from it, and you also get the best code. On this page, I'll outline a few scenarious, and explain the situation, and the initial code, and then what should be changed, to make it truly work. If you're already a good JS wiz, you probably will learn nothing new, but hopefully, newbies to JS will see some alternative ways, to solve old problems.

Making references right

This example is typical stuff you'll run into, when you're trying to hook up events to elements, via JS alone, and you want not just to handle the event, but also refer to some earlier variables (or state). Let's say you have a bunch of text boxes:

<input type="text" id="txt0" value="init0"><br>
<input type="text" id="txt1" value="init1"><br>
<input type="text" id="txt2" value="init2"><br>
<input type="text" id="txt3" value="init3"><br>
<input type="text" id="txt4" value="init4"><br>

You want to attach an event to each of these fields. We want it to do something with the value of the text box, as the event fires. We'll just alert it, but you could do anything of course.

field.onevent = function () {
    alert(this.value);
}

We want to write as little code as possible, so the script will look like this:

var elms = document.getElementsByTagName("input");
for (var i = 0; i < elms.length; i++) {
    elms[i].onchange = function () {
        alert(this.value);
    }
}

That's fine. The alert box will display the input boxs current value. How can we then do a compare, of the current value, and the original value of the field (maybe for validation, or something)? The first thing to try is:

var elms = document.getElementsByTagName("input");
for (var i = 0; i < elms.length; i++) {
    var originalvalue = elms[i].value;
    elms[i].onchange = function () {
        alert(this.value + " == " + originalvalue);
    }
}

Give it a try. You'll quickly see that it doesn't work. When you edit a field (and then leave it, firing the change event), it'll display: "currentvalue == init4". You wanted to make a copy of the original value, but why does it end up saying "init4" for all the fields? It should only say that for the last text field!

Getting closure

The problem is, that JavaScript has socalled function wide variable scope. I won't go into the deal here, there's plenty on that on the net. So, what is the solution? It's the function literal!

(alert)("test"); // try it, it works!

The code may look totally weird, but it's actually quite sensible. When you toss "alert" around, you're handling the function as an expression. Must languages allow you to put arbitary parenthesis around expressions. JavaScript likewise. And expression has a value. So when you put parenthesis around "2 + 2", as "(2 + 2)". The parenthesis return the value, to whatever is outside the parenthesis. Such as in:

1 + (2 + 2) + 3;

The "(2 + 2)" returns to the surrounding code as 4. You may as well have written "(X) + 3", where you add 3 to the result of an expression. Likewise, you can execute the expression, assuming it is a function being returned. A function literal (or an anonymous function) would be:

var x = function () {
    // do stuff
}

Here, the variable x is being assigned the value of the right hand side, which is a function literal. And combine this with the fact that JavaScript has function scope, we can solve the problem of variables being overwritten:

var elms = document.getElementsByTagName("input");
for (var i = 0; i < elms.length; i++) {
    (function () {
        var originalvalue = elms[i].value;
        elms[i].onchange = function () {
            alert(this.value + " == " + originalvalue);
        }        
    })();
}

It may look odd, but notice it has the same form, "(exp)();", and notice the calling notation. However, now notice that the variable originalvalue is set inside the (unnamed) function literal. Now you can ask why we didn't just set it inside the event handler, but that wont work, since when it runs, it will run in the scope of the event handler, and it will have no clue what elms[i].value means.

Here you can try for yourself, it works!