"fallenrogue" Under Leon's hat.

Sun Dec 20

JavaScript Part 5 - The Client Side

So, if JavaScript ever really had a true intention, I believe that intention was to bring static HTML documents to life. I’m sure if we read the history since 1995 (give or take a year) then we’ll notice that most work with JavaScript was aimed at giving the static web a much needed shot in the arm.

To understand how JavaScript works its magic we have to dive into the DOM or Document Object Model and it’s relationship to the document being rendered to the client.

DOM: The Document Object Model

At the top of the stack we have the object that does the presenting to the user: The Window aka The browser. This is represented as the Window object. A window is presenting one document at a time (I mean, you can’t have more than one URL at once, right? Well, frames but those are children of the main document.) and therefore Window has a property of Document.

window.document

or just…

document

Why can we just call on document? Because our top level object is implicitly the window. Pretty sweet, right? Making sense? I hope you said yes. If not, we’re just looking at the HTML document you requested from the server the same way JavaScript does… as objects. Now we know we’re speaking the same dialect but what else is added?

In plain English: The DOM is an object model for traversing and manipulating documents rendered by web browsers. There is a W3C standard for the DOM and the reason that JavaScript can hurt is because it is inconsistently implemented by browsers. So developers took to writing a lot of boiler plate “browser sniffer” code to ensure that behaviors were similar for all users viewing their sites. Internet Explorer is typically dragged through the mud because it’s version 6 of the browser was exceptionally slow to adopt DOM elements and events as standard and instead continued down its own path. But why? The answer is simple. IE 4 and 5 drove innovation in the DOM/JavaScript space. IE added additional attributes and events, e.g. drag and drop

As you can imaging there are various objects representing the HTMLElements that are part of the document (and some that may not be!) so we won’t list all of them here, I just wanted to let you know the top level context in client side JavaScript (window) so that you’re not surprised but what you’re going to see when we move to the bane of client side JavaScript: Events. (this is, without jQuery or your lib of choice!)

So, working with the DOM is made stupid easy when you’ve got help in the form or jQuery or MooTools or Prototype or some other lib that equalizes the DOM implementations of various browsers. Those folks are doing great work and I encourage you to use them (I certainly do!) for your work. BUT let’s look at what Mozilla’s Firefox does with HTML events so we can see how these event objects and the implicitly passed this contextual object cause confusion to the uninitiated.

First we have to establish an event to listen for. We do this in Mozilla’s flavor of JavaScript by doing adding a “listener” to a particular event. How about a click on a button? Easy enough. :)

Here’s the HTML document we’re going to use. Save it as an html page and open that document in Firefox. Open Firebug and follow along.

OK, first up is to grab a reference to the button element in the document.

var btn = document.getElementById("clickr");

Now, let’s create a simple function that will handle clicks the user makes on “clickr”.

function ourCallback(evt){
    alert(evt);
    alert(this);
};

Now let’s listen for clicks on btn.

btn.addEventListener("click", ourCallback, false);

If you’ve been reading along then it should come as no surprise the to types of objects that are alerted when we run this code in the Firebug JavaScript console.

The event object sent as an explicit argument is… [object MouseEvent] The this operator at this point gets the value of the implicitly passed sender… [object HTMLButtonElement]

So our button is this because it directly broadcasted the click and all listeners know who initiated the click.

That should make pretty good sense by now but what happens if the callback function has expectation on this that we’re not going to know at runtime? Here’s a case where things can be odd and cause lots and lots of frustration, well at least for programmers in the late 90s. :) Let’s create an object that simply counts a number every second. We’ll add an interval counter that alerts that value. Sounds simple right? It is, if you know what to expect when using this. Consider the following…

var ob = {count:1, currentCount: function(){ alert("my count is "+ this.count);}}
ob.currentCount();
setTimeout(ob.currentCount, 2000);

So, what’s going to happen when we run this sample? We’ve created an object to do our incrementing (even if we don’t have an incrementer for it… details!) for us. On line 2, ob calls currentCount and then on line 3 we ask the window to tell ob to call currentCount after 2 seconds. right? well, not so much, go ahead on run it. If you’re following along you’re going to see the following message alerted to the window:

my count is 1
my count is undefined

OH NOES!!! That’s right, friends, let’s read these same 3 lines of code the way JavaScript did… in plain english:

“Ok, the programmer created an object with one property and one method and assigned it to a variable named ‘ob’. Cool. I can do that. Next the programmer is asking ob to call currentCount. Cool, no problem. Finally they want the window to call currentCount in 2 seconds. done and done!”

You see, here we have our first curiosity caused by the simplicity and power of JavaScript. Remember when we said functions where higher order and first class citizens? Remember how “this” refers to the contextual object? Well if you do, then in plain english, what is happening here is that we’re delegating the function defined in ob.currentCount which is

function(){ alert("my count is "+ this.count);}

So, when we call the setTimeout method we’re just caching the function signature and not any reference to the parent object that references the function. So when the function is called after 2 seconds what is really being called is this:

window.currentCount(); 

But that’s not what we mean. We want ob to call it. SO, let’s not forget that other great feature of JavaScript: closures. Let’s instead, curry the exact state that we want in the form of a closure to be called at the timeout.

setTimeout(function(){ob.currentCount();}, 2000); 

Now when you run line 3, you should see the right alert message. WOW! Isn’t JavaScript cool?! I think so! Anyway, I hope that goes a long way to helping you understand what’s going on in the browser when you’re using JavaScript. Even though libraries like jQuery let us forget about these particulars when we code it’s nice to know what’s going on under the covers. :)

Comments (View)
blog comments powered by Disqus