A Programmer’s Tour Of Javascript

The Javascript Environment

Like most dynamic languages, JavaScript is not compiled into bytecode to run as an operating system executable. Instead, it is interpreted at runtime, and executed in a host environment. The interpreter and environment are collectively called the JavaScript engine.

The engine is usually part of a web browser, but this is not always the case. Server-side JavaScript (like Node.js) runs as an application on a Web server, as the name suggests. The same standard that defines JavaScript (ECMAScript) also standardizes ActionScript, the scripting language used by Adobe for Flash and AIR. JavaScript is also used in mobile device applications, using a “wrapper” framework such as PhoneGap (a.k.a. Apache Cordova) or RhoMobile.

But for this article, I’m going to assume that JavaScript is interpreted and executed by a web browser, since this is the most common use of JavaScript.

The fact that any web browser can run JavaScript is both good and bad. It’s good because it means JavaScript is everywhere: web browsers are on every desktop, and most mobile devices. JavaScript itself is just plain text, so you don’t need to install any additional software in order to write it. The fact that it is interpreted means that testing your code is as easy as refreshing your browser window. And the fact that it runs in a maganged environment means that you don’t have to worry about things like memory allocation.

But there are two main drawbacks:

  1. JavaScript is significantly slower than native code. This doesn’t matter so much for desktops or laptops, but it is crucial for smartphones. For details, read Why mobile web apps are slow by Drew Crawford. Note that this is a drawback common to all interpreted languages (PHP, Ruby, Scheme, etc.), not just JavaScript. This is also improving, as vendors are all engaging in “engine races” against each other, though they will probably never reach the same speeds as native code.
  2. Each browser has its own “flavor” of JavaScript. The browser may disobey standards, implement its own version of standard objects, or offer vendor-specific features as “improvements.” This means that you often have to do tests to see if a feature is supported, and sometimes write entirely different code. Yes, Internet Explorer, I’m talking about you. This was especially bad in the “browser wars” of the late 1990’s and early 2000’s, but things have stabalized considerably – though not entirely.

Of course, the JavaScript engine is only one part of a web browser. The parts of the browser that are responsible for interpreting and displaying HTML and CSS are entirely different. The HTML engine generally ignores errors coming from the JavaScript engine. This means that if your JavaScript code fails, your web page will still be displayed. However, it also means that those failures are invisible.

Debugging JavaScript

Modern web browser provide a JavaScript console so you can see any errors, and also so you can directly execute JavaScript code. The console is provided as part of a suite of web developer tools. These are usually displayed as a tabbed window beneath the main browser window. Each browser has a different shortcut to access the JavaScript console directly. Chrome and IE open the developer tools when you press F12. Firefox From there, you just click on a tab to bring up the console.

You can interact with the browser’s JavaScript console using the surprisingly-named console object. This is not part of any standard, so each browser’s version is slightly different. All of them, however, support the log() method, which simply prints its parameter(s) to the console output as a string. That will do for most people.

Here are direct links to each vendor’s web developer tools documentation:

The standard console prompt is “> “, and when you execute a JavaScript expression at the prompt, the value of the expression is displayed on the next line. I will use this prompt in this article to refer to JavaScript that is executed by the console; code that does not have this prompt is assumed to be executed by the web browser already.

Now, the console is a very useful tool; and if you have a browser that is not ancient, you have it already. But there are a number of tools that are more advanced (and more specialized), which serious JavaScript programmers may want in their toolbox. For code analysis, you could use JSLint or its community fork JSHint (just make sure you use it for Good, not Evil). For unit testing, you can use Jasmine or Unit.js. To automate those tests, you can use Grunt. This is by no means an exhaustive list, and new tools are popping up all the time.

JavaScript and HTML

So, how do you execute JavaScript code in the browser? You can’t just open a JavaScript file directly; the browser will display the contents of the file as plain text. Instead, you need to place JavaScript code into a displayed document, almost always an HTML file. There are three ways to do this, and two of them are Very Bad Ideas.

Inline JavaScript
This is a JavaScript function that is hard-coded into the attributes of HTML tags. Here’s an example:

<a href="javascript:goSomewhere()" onclick="doSomething()">Click Me!</a>

Why this is bad: First, there’s no separation of concerns; you’re mixing up presentation (HTML) and control (JavaScript). Second, it is harder to maintain – think of how many tags you would have to change of you changed the name of the doSomething() function. Third, it won’t degrade properly if users disable JavaScript in their browser. So in the example code above, clicking on the link would do nothing.

JavaScript in the header
This is when you put entire JavaScript code inside the header of the HTML document. Here’s an example:

<html>
  <head>
    <script type="text/javascript">
    // ...some JavaScript code...
    </script>
  </head>
  <body>
  ...the rest of the HTML page...
  </body>
</html>

Why this is bad: It has all of the drawbacks of inline JavaScript, and more. It will increase page load times for three reasons: the HTML file will be bigger, the web server won’t be able to minimize the JavaScript, and the browser won’t be able to cache the JavaScript separately. More importantly, you won’t be able to re-use code between HTML files.

JavaScript in a separate file
This is when you keep your JavaScript in a completely separate file, and import it into the HTML document using the src attribute of a script tag:

<script type="text/javascript" src="my_code.js"></script>

This is the only method you should use. As a general rule, you should never include a single line of JavaScript in your HTML files.

There are some edge cases where inline JavaScript can improve performance, but they are pretty rare. If you are in a situation where you encounter one of those edge cases, then you probably work for Google or Yahoo! or something.

For details, read Why Inline CSS And JavaScript Code Is Such A Bad Thing by Robert Nyman.

JavaScript environment objects

Because JavaScript is interpreted, it does not interact directly with the operating system. This is mainly done for security reasons. This means that you can’t open files on the hard drive, listen for keyboard strokes, or what have you. Instead, it interacts with the interpreter, using objects to model the interpreter’s environement.

Every JavaScript interpreter has some sort of global object that represents the global state of the interpreter. In web browsers, the global object is the window object. This is often called the “global namespace,” though that term may be confusing, because the global object is nothing like a C++ namespace or Java package (neither of which are objects in memory).

Because the window object is the global object, any “stand-alone” functions or variables are actually methods or properties of the window object. So, there is no difference between, say, window.alert("Hello, world!") and just alert("Hello, world!"). This does not work with other browser objects – at least, not in all browsers.

Besides the global object, there are two different object models that JavaScript uses to interact with the browser environment. The first is the Browser Object Model (BOM). The second is the Document Object Model (DOM). Like everything else, the objects in both of these models are all “owned” by the global window object.

The BOM is a group of objects that represent the state of the browser itself. The BOM objects are the navigator, screen, history, and location objects. (Technically, the window object is also considered part of the BOM.)

Generally speaking the BOM isn’t very useful. Historically, it was used to do things that we now recognize are absolutely horrible from a user’s perspective. Things like opening pop-up windows, resizing the browser, involuntary page redirection, opening alert boxes, and so forth. Modern browsers actually block most of these things from happening. Even if not, doing any of these things is pretty much guaranteed to get your website put on someone’s blacklist.

Another historic use of the BOM is for “browser sniffing” – examining the BOM to see if the visitor is using a specific browser (e.g. Internet Explorer 5.5), and customizing your JavaScript methods accordingly. But this is bad practice; you should do feature detection instead. For more on this, read How to Detect Features Instead of Browsers on the IE developer site.

This brings up another issue with the BOM: unlike the DOM, it’s not standardized. At this stage, most of the BOM is cross-browser compatible, but there will always be parts of the BOM that are not. It’s just one more reason to avoid it.

The DOM is a group of objects that represent the document currently being displayed in the browser. This is usually an HTML document, though it may also be XML. The DOM is accessed through the JavaScript document object. The DOM is the real “meat and potatoes” of JavaScript; nearly all JavaScript code is designed to interact with the DOM.

The DOM considers everything to be a type of node object. Each HTML (or XML) tag is represented by an element object. DOM elements are organized like a tree, and this tree represents the nesting of one tag inside another – say, a td tag that is nested inside a tr tag, which is itself nested inside a table tag. This means that most elements have other elements that are children, siblings, or a parent of the current node. Each element object has properties that can access these other elements, and methods that allow you to modify the tree by adding or deleting elements.

But this isn’t all that common, because it would mean that your JavaScript code is limited to documents with the same (or a very similar) tree structure. It is far more common to set the ID attribute of a tag, and to access that element by searching the DOM for that ID. This is usually done through the document.getElementById() method. (The document.querySelector() method does pretty much the same thing, but is not supported in “older” browsers, like IE 7.)

You can also get elements by their name attribute, using the document.getElementsByName() method. But you shouldn’t use the name attribute, for a couple of reasons. First, HTML names (unlike ID’s) are not guaranteed to be unique. Second, they were only designed to be used on certain HTML tags (mainly form inputs), but confused web designers started adding them to everything. Because they are ambiguous and wrongly used, name attributes were deprecated as of XHTML 1.0 (2002). I only mention this because they’re in lots of legacy code, and because bad designers still use them sometimes.

Once an element object is selected, its contents can be changed by setting its innerHTML property. Naturally, different types of elements have different properties that can be read or set; you can get the value property of an Input Text object, or set the src property of an Image object.

All visible objects have a Style object associated with them. This can be accessed using the element’s style property. You could use this object to set the object’s background color, text color, borders, etc.

However, this is a bad idea, as it doesn’t separate presentation from control. A better idea is to create a CSS class, and to dynamically assign that class to the DOM element. You do that by setting (or appending) the class to the element’s className property. Here’s an example:

var node = document.getElementById("myElement");
// Replaces the current class(es) of element
node.className = "some_css_class";
// Adds a class to the element's existing class(es)
node.className += " another_css_class";

Elements are not the only things that are considered “nodes” in the DOM. An HTML element’s attributes are considered Attr objects, which are accessed through the element’s attributes property. And if the HTML tag contains text (like a p tag), its DOM element will have a Text object as a child node.

The document object also contains four array-like objects that are left over from before the DOM was standardized. (This is usually referred to as “DOM Level 0.”) They collect all of the tags of a certain “kind” together, and are ordered according to their top-to-bottom position in the document. They are:

You can get a child element (e.g. a form input) using its index or its element ID (or its name, if you’re refactoring legacy code). Here’s an example of an HTML form:

<form name="myFormName" id="myFormId">
  <label for="myInputId">Name:</label>
  <input type="text" name="myInputName" id="myInputId" />
</form>

You could set the input text value to “my value” in any of these ways:

// ID as property
document.forms.myFormId.myInputId.value = "my value";
// Element index
document.forms[0].elements[0].value = "my value";
// Name as property - deprecated!
document.forms.myFormName.myInputName.value = "my value";

The DOM Event Model

Executing JavaScript code is usually triggered by some event. In most cases, this event comes from some kind of user interaction (like clicking on an element), but not always.

Under the “traditional model” of event registration, events are members of their DOM elements. You can assign a function to that event member, and that function will be called whenever that event occurs. The function becomes an event listener or event handler when it is associated with a specific event. A listener is said to be attached to, or registered with, its element. For example, let’s say that you had an HTML button, like so:

<button id="myButton">Click Me!</button>

Using JavaScript, you could assign a function to the button’s onclick property, and that function would be attached to the button as listener for the onclick event. Here’s some JavaScript code that annoys the user by popping up an alert box saying “Boo!”:

function annoyUser() {
  alert("Boo!");
}
document.getElementById("myButton").onclick = annoyUser;

The “traditional model” has been around since 1996 (hence the name), and is supported by every single browser that came out since then. But it has a huge disadvantage: you can only assign one function at a time to the element’s event member.

If you want to add an additional listener for a specific event, you should use the addEventListener() method, like so:

document.getElementById("myButton").addEventListener("click", annoyUser);

Note that you leave out the “on” prefix in the event name.

The addEventListener() method takes a boolean as an optional third argument, but what it does requires some explanation. Remember that HTML elements are nested, and each one of those elements can have event listeners. Let’s take a simple HTML form with nothing but a single submit button:

<form id="myForm">
  <button id="myButton" type="submit">Submit</button>
</form>

Now, we’ll attach some event listeners:

function annoyForm() {
  alert("Form!");
}
function annoyButton() {
  alert("Button!");
}
document.getElementById("myForm").addEventListener("click", annoyForm);
document.getElementById("myButton").addEventListener("click", annoyButton);

If the user clicks on the button, then both events are fired, and the user will be annoyed by both alert boxes. This is because the event propagates from one element to the next. The question is which one will annoy the user first. Is it the “Form!” alert followed by the “Button!” alert, or the other way around?

There are two possible answers to this question. The event may propagate from the button element to the form element; this is called event bubbling. Or, it may propagate from the form to the button; this is called event capturing. Before JavaScript was standardized, IE used bubbling, and Netscape used capturing. (If you’re wondering what a “Netscape” is, ask your parents.)

The third argument to the addEventListener() method determines whether the event should be bubbled or captured. Passing true means that events should be captured; false means they should be bubbled; and passing no value results in the browser’s default behavior. Nowadays, everyone uses bubbling (which makes more sense, IMO). But for legacy reasons, it’s a good idea to always pass false as the third argument.

If you want to stop propagation altogether, you can create a function that accepts an Event object. The object that is passed to this function will have a method called stopPropagation(), which (as you can imagine) will stop event propagation. Let’s rewrite the above code so that only the “Button!” alert will annoy the user:

function annoyForm() {
  alert("Form!");
}
function annoyButton(e) {
  alert("Button!");
  e.stopPropagation();
}
document.getElementById("myForm").addEventListener("click", annoyForm);
document.getElementById("myButton").addEventListener("click", annoyButton);

Now, the alert dialog will say “Button!” when the user clicks on the button, and “Form!” when the user clicks on any other part of the form.

Of course, as I said earlier, events don’t just come from the user. For example, the window.onload event is fired when the page finishes loading in the browser. This event is incredibly useful, because when it fires, it is guaranteed that the DOM is fully created. If you attempted to access any of the DOM elements before then, they would be undefined. This is the situation if your JavaScript source file is imported in the HTML document’s head section.

It is fairly common to have a single function that acts as an entry point for your entire JavaScript program, and is fired when the page loads. Let’s see an example:

function main() {
  // ...entry point of program
}
window.addEventListener('load', main, false);

I called the function main to follow the convention of languages like C, C++, and Java, but it could be named anything you like.

Before I finish talking about event registration, I have to mention some cross-browser compatibility issues.

The addEventListener() method is part of the DOM Level 2 specification, released in 2000, and almost all browsers have supported it since then. Of course, by “almost all browsers,” what I really mean is “all browsers except Internet Explorer.” It was not supported until IE 8; prior to that, Microsoft used a nearly-identical method called attachEvent(). Unlike the DOM method, the IE method includes the “on” prefix, and it does not take a third argument.

Also unlike the DOM specification, event objects in IE 8 and below do not support the stopPropagation() method. Instead, they have a cancelBubble property, which can be set to false.

Given the above, let’s re-write the JavaScript code above to make it compatible with all versions of all browsers. We’ll use feature detection, which I mentioned earlier. If a feature is available, it is used; if not, an alternate feature is used. Trying to access a non-existent method or property will return undefined, and in JavaScript, undefined evaluates to false. On the other hand, any non-null object (including a function object) evaluates to true. So, to see if a function is available, we can simply put the function name in the parentheses of an if expression.

function main() {
  // ...entry point of program
}
if (window.addEventListener) {
  // Most browsers
  window.addEventListener('load', main, false);
} else if (window.attachEvent) {
  // "We're sorry, there's no money in the IT budget for new computers"
  window.attachEvent('onload', main);
} else {
  // "Old Man Yells At Cloud"
  window.onload = main;
}

Another big issue concerns the use of this in event handlers. When I went over the this keyword earlier, I mentioned that within DOM event listeners, this usually refers to the DOM element object that fired the event. So if, say, an HTML button fires an event, and your function is a listener for that event, then this refers to the DOM Button object that fired the event.

Unfortunately, this pattern is not universally followed. It is always followed when this appears in inline JavaScript – statements or function calls that appear in an HTML event attribute like onclick. (But once again, you shouldn’t do this.) It is also the rule when an event listener is attached using the addEventListener() method. But if you use the old IE-only attachEvent() method, this will refer to the global window object.

As a workaround, you can use explicit binding. Put your function call inside another function, and use the apply() method:

// Adds the "red" CSS class to the element
function makeRed() {
    this.className += " red";
}
// The element whose class we want to change
var elem = document.getElementById("myElement");
// Binds function's "this" to element in IE < 9
elem.attachEvent("onclick", function(args) {
    makeRed.apply(elem, args);
});

For more information, read this as a DOM event handler on the Mozilla Developer Network, and The this keyword on QuirksMode.

There are a ton of different events that are fired on all sorts of elements. The most commonly used ones are probably onclick, onmouseover, and onmouseout for mouse events; and onblur and onsubmit for form verification. But there are many, many more. I recommend that you spend an hour or two reading through the W3Schools reference on HTML DOM events.

Advertisements

About Karl

I live in the Boston area, and am currently studying for a BS in Computer Science at UMass Boston. I graduated with honors with an AS in Computer Science (Transfer Option) from BHCC, acquiring a certificate in OOP along the way. I also perform experimental electronic music as Karlheinz.
This entry was posted in JavaScript, Programming and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s