Purchase Advertising On AWT
 Upload & Sell Your Software
 Upload & Sell Your eBook
 Enter The AWT Control Panel  Contact Applied Web Tools
 Sell Your eBook @ The PDF Store  Password Troubles? Click Here!
Knowledge Center
Want to write for Applied Web Tools? Send your Writer's Query to
Editor@AppliedWebTools.net for details.
HTML
Reference
&
Tutorial
CSS
Reference
&
Tutorial
DOM
Reference
&
Tutorial
JavaScript
Reference
&
Tutorial
50+ Programming Examples
Use HTML, CSS, DOM & JavaScript
Misc. Essays & Documents
You'll Find Just About Anything In Here
The DOM
Document Object Model
29 pages, 11325 words
This document takes 20 to 35 minutes to read
In this chapter we'll cover the Document Object Model, or DOM. We’ll start by addressing what the DOM is and how it has evolved over time. Then we will explore its internal structure and exposed properties and methods in general, followed by a detailed discussion of working with the DOM using JavaScript.

At the end of this chapter you should have a solid understanding of the DOM and its inner workings. You should also have some familiarity with the standards governing it and where to find them, as well as how they evolved. You should understand not only how to handle user events in your scripts, including how to fire them manually, but also how to make custom events of your own.

How I Learned to Stop Worrying and Love the DOM

The Document Object Model is just that: an object that models the document currently loaded into the browser. Every single element in the document will have a corresponding presence in the DOM: every paragraph, every list item (and every list), every span, and so on. This includes elements that might not be visible in the rendered document, such as <script> tags, style sheets, and so forth. Even the document’s title will be in the DOM. If it is in the document’s markup, it will have a presence in the DOM. In addition, the DOM presents many useful properties for accessing and manipulating these elements.

The DOM also provides an event model for capturing user interactions with the document: keypresses, mouse movements, and so forth. Using DOM events, you can write scripts that respond to user interactions, from simple things like highlighting a paragraph when the user clicks it, to dragging and dropping elements on the screen.

DOM !=JAVASCRIPT

JavaScript often comes under fire for being difficult to work withwhen really the specific problem is actually related to the DOM and not JavaScript itself. It’s important to understand that the DOM isn’t JavaScript. The DOM is an interface to the document provided by the browser manufacturer. The two are closely intertwined and many people make the mistake of assuming they are the same, but they are not.

It’s easy to conflate JavaScript and the DOM because most of what you will be doing with JavaScript in the browser will involve manipulating the document that the browser has loaded. But if you were to access a JavaScript interpreter in a different context—for example, using Node.js on a server or workstation—you wouldn’t necessarily have a DOM to access.

The DOM is not actually part of JavaScript, nor is it defined by the ECMA-262 standard. Instead, the DOM is governed by its own standard, maintained by the World Wide Web Consortium (W3C). Even though the DOM is not part of JavaScript itself, no JavaScript reference would be complete without mentioning it. Much of what you will be doing in browser-based JavaScript will involve working with the DOM. In fact, the DOM and JavaScript are so tightly intertwined that it’s not unusual to see less experienced JavaScript developers refer to DOM features as JavaScript features.

The DOM standard came about directly because of the so-called "browser wars" at the beginning of the Web’s history. Back then, browser manufacturers decided how they wanted to parse HTML documents and what (if anything) they wanted to expose to their JavaScript engines. As a result, web development was a nightmare of browser-dependent code and "best viewed in Netscape" and "best viewed in Internet Explorer" banners.

In response, the industry decided to standardize not only the languages that were being used to build the Web (HTML, CSS, and JavaScript) but also how browsers should implement those languages. This promised to level the playing field across all browsers, making it possible to leave behind browser-dependent coding.

The realization of this promise took some time, however. Standardizing the Web was a huge undertaking and, in many ways, is a process that will never be truly "finished." Technologies are continuing to evolve, resulting in standards that change with them, which is hard on the browser manufacturers because they have to aim at multiple moving targets. Even so, the DOM and other web standards paved the way for the modern Web as we know it.
History of the DOM Standard

As previously mentioned, the DOM is governed by its own separate standard, which is owned by the W3C. The DOM standard was originally developed in three main iterations, Level 1, Level 2, and Level 3.

The Level 1 DOM standard was the first to be proposed and provided the foundation for everything else. It had two main features: a generic set of low-level interfaces for representing any structurally marked-up document, and extensions to that generic specification for HTML documents in particular.

HTML:DESCRIPTIVE, STRUCTURAL, SEMANTIC

In general, markup languages are a family of languages that specify a syntax for annotating documents. Broadly, markup languages fall into three categories:

Presentational : Annotations are typically of a low-level (and often not human-readable) format and are used to specify how applications should display the content. Presentational markup is often used by word processors, for example.

Procedural : Similar to presentational, but annotations are of a higher level format. Annotations are typically human readable, and are intended to specify how content should be displayed. Examples include PostScript, troff, and TeX.

Descriptive : Annotations are used to describe individual parts of the document based on their properties. How that content should be displayed is left up to the interpreting application. Examples include Scribe, SGML, and HTML.

HTML’s tags annotate content according to their individual structural properties, so HTML is often referred to as a "structural" markup language. It is also often referred to as a “semantic” markup language, because many of the structural annotations refer to the semantic meaning of their target content, such as <p> for paragraph, or
for a heading. However, some HTML tags are semantically null and refer only to structure, such as
or tags.

The Level 2 DOM standard followed quickly and provided much more depth than Level 1. It actually consists of six separate specifications:

Core : Extends the Level 1 DOM core specification and includes new interfaces for XML. The specification is at www.w3.org/TR/DOM-Level-2-Core/.

Events : Provides the event model that is implemented in most modern browsers. See DOM Events, below, for specifics. Link: www.w3.org/TR/DOM-Level-2-Events/

HTML : Extends the Level 2 Core specification with features specific to HTML and XHTML. Link: www.w3.org/TR/DOM-Level-2-HTML/

Traversal and Range : Provides interfaces for identifying ranges of content within a document, as well as interfaces for moving through the DOM: for example, starting with a given element, find all of its siblings, or all of its children. Link: www.w3.org/TR/DOM-Level-2-Traversal-Range/

Style : Provides interfaces for a document’s Cascading Style Sheets (CSS), as well as the styles that have been applied to elements. Link: www.w3.org/TR/DOM-Level-2-Style/

Views : Specifies the views that are presented by the browser to its scripting engines, including JavaScript. Link: www.w3.org/TR/DOM-Level-2-Views/

The Level 3 DOM specification was written as an extension to Levels 1 and 2. It consists of five separate specifications:

Core : Serves as a further extension of Level 1 and Level 2 Core, providing new interfaces and methods. Link: www.w3.org/TR/DOM-Level-3-Core/

Events : Extends the Level 2 Events specification, providing event specifications for keyboard and mouse wheel events, as well as mutation events (events that fire when a DOM node is modified somehow). Link: www.w3.org/TR/DOM-Level-3-Events/

Load and Save : Specifies how to parse XML and produce a DOM tree from it, and how to serialize a DOM tree into XML. Link: www.w3.org/TR/DOM-Level-3-LS/

Validation : Specifies how to keep documents internally consistent as they are changed by various methods. Link: www.w3.org/TR/DOM-Level-3-Val/

XPath : Provides a specification for using XPath to access DOM trees. Link: www.w3.org/TR/DOM-Level-3-XPath/

Finally, the HTML5 family of specifications, which is a W3C Candidate Recommendation as of December 2012, includes DOM specifications that are meant to subsume much of the material in the previous specifications.

At this point, there are a dozen or so documents specifying the DOM standard, some of them subsuming others wholly or partially. To avoid confusion, and to avoid inadvertently tying the DOM standard to the HTML5 language specification (you should be able to use the DOM standard with any structural markup language, not just HTML), the work was begun to consolidate all the disparate parts into one specification. You can view the results in the following two places:

The December 2012 Working Draft of the DOM4 (http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html) at the W3C. This version provides more context as to how the various versions have been merged together, including future plans for consolidation.

The Living DOM Specification (http://dom.spec.whatwg.org/) at the Web Hypertext Application Technology Working Group (WHATWG).

The WHATWG is a separate group from the W3C that was formed in response to concerns about the W3C’s methodologies. A certain amount of tension exists between the two groups, but ultimately they have the same goals, and though they maintain separate versions of the DOM specification, their versions should never disagree. We encourage you to review both versions; they provide slightly different views of the same information.
Browser Dependencies

Just because governing standards exist for the DOM doesn’t mean all browsers implement them equally. The DOM standards arose over time, driven by the need to rein in disparate and diverging document model implementations by the browser manufacturers, and even today that standardization is a work in progress. The JavaScript we have studied in the first two chapters is fairly stable across browsers. With the DOM, we will be encountering variations in implementations from browser to browser, and even from version to version of the same browser. Unfortunately, these variations are often quite pronounced and, if left unaddressed, can result in code that will run in one browser but stubbornly refuse to run in others.

The good news is that modern browsers (Internet Explorer 9 and later, Firefox 4 and later, Chrome, Safari, and Opera) implement these standards very well. Older versions of Internet Explorer are the worst offenders for bad implementations, and unfortunately you will often have to provide support for these older versions in your projects. Most often these implementation problems can be mitigated with JavaScript. Even better, there are already dozens of excellent, well-tested JavaScript libraries that do exactly that.

Probably the most popular of these libraries is jQuery, originally created by John Resig and now maintained as an open source project. According to some browser statistics, jQuery is the most widely deployed library on the Web, so it is definitely well tested. We’ll cover using jQuery a bit in Chapter 4.

jQuery isn’t the only library available; there are plenty of others. One of the first libraries created is called Prototype, which is still widely used and is also an actively maintained open source project. Other libraries of note are Dojo Toolkit, script-aculo-us, MooTools, and Yahoo’s YUI Library. And if you want something that provides more support—something more like a framework—there are choices like Sencha Ext JS, Closure Library and AngularJS (both by Google), Backbone.js, and Montage (a newcomer to the JavaScript frameworks scene, but a favorite of ours).

In this chapter we’ll be covering the DOM from a standards viewpoint. When we encounter serious divergences in implementation, we’ll mention them, but it’s safe to assume that the older a browser version is, the greater the chance that it won’t implement some aspect of the standards correctly. We’ll also discuss mitigation techniques, so you should be able to work through the worst of the problems. Overall, though, if you find yourself in a situation where you have to support many older browser versions, you may find that a well-chosen JavaScript library will prevent many headaches.

If you will be supporting older browser versions, you’ll want to assess how well they support the features you’ll be needing for your project. Peter-Paul Koch maintains two excellent pages on his QuirksMode web site, one for DOM features (www.quirksmode.org/dom/w3c_core.html) and one for DOM events (www.quirksmode.org/dom/events/). These pages are a great starting point for assessing your browser support situation.

DOM Structure

Now that you know what the DOM is and how it came to be, you are ready to dive into how it actually works for you. Conceptually, a DOM structure can be thought of as a tree that represents the documents and subdocuments that are loaded into the browser, as well as the individual elements of each document and subdocuments.

The top of the structure is the window object, which represents the actual browser window containing the loaded document. Since HTML documents can contain subdocuments through the use of iframes, the window object is actually an array-like object. It has a length property, representing the number of iframes the document contains, and these iframes can be accessed via indexes (e.g., window[0]) or, if the <iframe> tags have name attributes, via window[‘iframename’]. The window object also has a frames property that is an array representing the number of iframes present in the document. It is just a reference to the window object itself (in other words, window === window.frames and window[0] === window.frames[0] if a subdocument was present), but it has the virtue of being more explicit. Either syntax will work.

Each subdocument will have its own window object, and since any given subdocument can itself contain subdocuments, a subdocument’s window object will also be an array-like object.

The window object has several other useful methods and properties. The window object also serves as the global context for JavaScript, as demonstrated in Listing 1.

Listing 1. The window Object Is the Global Context for JavaScript

myVar = 5; // defined without var keyword, so it is global

alert(window.myVar); // will alert 5


Since window provides the global context for JavaScript, you do not need to access the document object explicitly as a property on the window object; instead, you can access it directly (in other words, referencing window.document. title is the same thing as referencing document.title anywhere in your script).

The window object has a document property that is a reference to the document that has been loaded into the browser window. The document object contains the tree representing the elements of the document itself. Every paragraph tag, every span tag, every div tag, and even script tags, the HTML and body tags, and the document title will all have a presence in the document object. If it’s in the HTML markup, it will be represented in the document object. In addition, the document object has several methods for accessing elements, and provides the base for events.

Structurally, the DOM consists of nodes, with each node representing content (usually a tag, but can also represent comments and metadata) in the HTML document. Because HTML is structural, nodes are organized structurally: an element’s node in the DOM can have child nodes that represent tags that the element itself contains. Similarly, a node in the DOM can have a parent node that represents that element’s parent tag.

Consider the simple example document shown in Listing 2.

Listing 2. Simple HTML Document

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

alert(document.title); // will alert “JavaScript Programmer’s Reference”

</script>

</body>

</html>


HTML5 EXAMPLES

Listing 2 is the first example where we have shown an entire HTML document instead of just a JavaScript snippet. Because DOM manipulation involves working with HTML documents, we’ll be using this format for our examples in this chapter. In particular, we’ll be using HTML5, as specified by the DOCTYPE tag.

Note that if you are using a particularly old browser version (e.g., Internet Explorer 6 or Firefox 2), it might have problems with these examples. (If you are using such an old browser version, we encourage you to upgrade if at all possible.)

If you load this document, the browser will alert “JavaScript Programmer’s Reference” because the title is referenced by the document.title property.

The nodes within the body are referenced by the childNodes property on the document object:

alert(document.childNodes); // Depending on browser, will alert something like “[object NodeList]”

Nodes within the DOM are represented by array-like node lists. They have a length property representing how many nodes are present, and the individual nodes themselves can be accessed via their index.

Each individual node will have properties that vary depending on the element it represents, but essentially any attribute on the element will have a representation as a property on the node. For example, the class attribute on a paragraph tag will be represented in the className property on its node.

Using this tree of nodes, you can access any element in the DOM. For example, document.childNodes[1].childNodes[2].childNodes[3] represents the second paragraph in our markup:

alert(document.childNodes[1].childNodes[2].childNodes[3].className); // will alert “hideme”

Individual nodes have a reference to their parent via the parentNode property. Since a tag can have only one parent, the parentNode property is not an array-like collection but rather a simple property:

alert(document.childNodes[1].childNodes[2] == document.childNodes[1].childNodes[2].childNodes[3].

parentNode); // will alert “true” because the body tag is the parent node of the paragraph

That’s the basic structure of the DOM, but it’s pretty unwieldy when it comes to accessing elements. You can access anything, but even with our super-simple document, we’re already producing fairly long reference chains. Imagine how long those chains would be if we had a complex document! Fortunately, the DOM provides better ways to access elements than through these long reference chains.

Accessing Elements in the DOM

Typically you want to access an element within the DOM directly and do something with it: hide it, show it, move it, delete it, make it listen for an event, and so on. The DOM provides several different methods for accessing elements either directly or by starting from a known place and traversing the tree.

Probably the most famous and easiest way to access an element is to use the document.getElementById() method, as shown in Listing 3.

Listing 3. Using getElementById()

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

var myPar = document.getElementById(“myParagraph”); // myPar is now a pointer to the paragraph.

myPar.innerText = “I have changed the content!”; // Change the text of the paragraph to “I have changed the content!”

</script>

</body>

</html>
In this example, we are getting a pointer to a specific node and then updating its innerText property with new content. When this document loads, you will see two paragraphs: the first will read “I have changed the content!” and the second will read “Another paragraph!”

In Listing 3, the document.getElementById() method and innerText property are specified by the DOM standard rather than the JavaScript standard. Since they integrate so seamlessly with JavaScript in the browser, you can see how easy it is to mistake them for JavaScript, as mentioned previously.

There are several other methods that the DOM exposes for accessing nodes directly. The getElementsByTagName() method takes a tag name as a parameter and returns a collection of all the nodes it finds in the document that are that sort of tag, as shown in Listing 4. The collection is an array-like object, so you can iterate over the individual elements, which are presented in order of parsing in the document, as demonstrated in Listing 4.

Listing 4. Using getElementsByTagName()

<html>
<head>

<body>

<p id=“myParagraph”>This is my paragraph! Lorem ipsum dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p> <script>

var myPars = document.getElementsByTagName(“p”),

mySpans = document.getElementsByTagName(“span”),

myParsLength = myPars.length,

mySpansLength = mySpans.length,

i;

// Give paragraphs a red background color

for (i = 0; i < myParsLength; i++) {

myPars[i].style.backgroundColor = “red”;

}

// Add some content to our empty span, and alert its index

for(i = 0; i < mySpansLength; i++) {

if (mySpans[i].innerText === ““) {

mySpans[i].innerText = “No longer empty!”;

alert(i);

}

} </script>

</body>

</html>


Listing 4 will change both paragraphs to be red, add the text “No longer empty!” to the empty span, and alert the number 1, which is the index of the empty span in the collection of spans returned by the getElementsByTagName() method.

Prior to HTML5, getElementById() and getElementsByTagName() were the two methods the DOM standard specified for accessing elements directly. The HTML5 specification adds three new methods: getElementsByClassName(), querySelector(), and querySelectorAll().

The getElementsByClassName() method works like getElementById(), except it takes a class as a parameter and returns an array-like collection of elements that have that class, as shown in Listing 5.

Listing 5. Using getElementsByClass()

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

var hideme = document.getElementsByClassName(“hideme”),

hidemeLength = hideme.length,

i;

// Hide all the elements that have a class of “hideme”

for (i = 0; i < hidemeLength; i++) {

hideme[i].style.display = “none”;

}

</script>

</body>

</html>


In Listing 5, we are getting all elements that have a class of “hideme” and then hiding each one by changing its CSS display attribute to “none.”

The querySelector() and querySelectorAll() methods provide a lot more flexibility than the other methods. Both methods take any valid CSS selector as a parameter. The querySelector() parameter will return the first element (in markup order) in the document that matches, while querySelectorAll() will return an array-like collection of all elements that match the selector (if there is only one match, the method will return an array-like object with one member; if there are no matches, the method will return an array-like object with no members—that is, a length of 0). This gives us a powerful tool for accessing elements in the DOM, as demonstrated in Listing 6.

Listing 6. Using querySelector()

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

var emptySpan = document.querySelector(“#myParagraph span:last-child”); // Get the last span emptySpan.innerText = “Not empty anymore!” // And give it some text.

</script>

</body>

</html>


In this example, we access the last span of the paragraph using the CSS pseudo-selector last-child. When it is run, this example will place the words “Not empty anymore!” in that last span.

One of the interesting things about using querySelector() and querySelectorAll() is that the elements you want to access with JavaScript often are the same elements you want to access with your style sheets. Thus, often you will find that some of the same selectors you’re using in your CSS will show up in your JavaScript as queries.

Caution : If you intend to write code that relies on querySelector() or querySelectorAll() and are planning to support older browser versions,those versions may not provide these methods for you because the methods arerecent additions to the DOM standard. In that case, you could always use aselector library to add the feature. The most commonly used selector engineis called Sizzle, available at www.sizzlejs.com/. It has no dependencies onother libraries and is quite small, very efficient, and well tested (it isthe engine included in the jQuery library).

Finally, each node in the DOM possesses these element selection methods, just like the top-level document object does. When you use one of these methods on a DOM node, the method’s scope is limited to the children of that node, as you can see in Listing 7.
Listing 7. Using the Methods on an Element

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

var myPar = document.getElementById(“myParagraph”), // Get a reference to the first paragraph

mySpan = myPar.querySelector(“span:last-child”); // Get a reference to the last span in that paragraph

mySpan.innerText = “Not empty anymore!”

</script>

</body>

</html>


This example, like Listing 6, will add the text “Not empty anymore!” to the last span of that paragraph. In this case, we first get a reference to the paragraph using getElementById() and then search just that paragraph’s child elements using querySelector(). This applies to DOM fragments as well (see the next section for details on DOM fragments).

Traversing the DOM

Another way of accessing elements in the DOM is to start at a known place in the DOM tree and then traverse to a different location using parent/child/sibling relationships. We’ve already seen a basic example of that in the previous section. Fortunately, the DOM provides some convenient properties for traversal:

Node.firstChild: Reference to the first child of the node

Node.lastChild: Reference to the last child of the node

Node.nextSibling: Reference to the next sibling of the node

Node.previousSibling: Reference to the previous sibling of the node

Listing 8 shows an example of these traversal properties.

Listing 8. Using Traversal Properties

<html>

<head>
<body>

<p id=“myParagraph”>This is my paragraph! <span class=“hideme”>Lorem ipsum</span> dolor sit amet.</span></p>

<p class=“hideme”>Another paragraph!</p>

<script>

var mySpan = document.getElementById(“myParagraph”).lastChild;

mySpan.innerText = “Not empty anymore!”

</script>

</body>

</html>


This example will fill in the last span in the first paragraph with the text “Not empty anymore!” Now that you know how to access elements in the DOM, the following section covers what you can do with them.

Modifying the DOM

In addition to enabling you to access elements in the document, the DOM provides a flexible framework for manipulating those elements. You can modify existing elements by changing their properties, changing their content, or even moving them completely from one place in the DOM to another. You can also delete elements and create new ones.

Modifying Existing Elements

Probably the most basic modification you might want to make to an existing element is to access and change its properties. Most simple element properties are presented as simple properties on the element’s associated node, and you can get and set the values directly.

For example, Listing 9 shows how to change the href property of a simple anchor tag directly.

Listing 9. Modifying an Element’s Properties

<html>

<head>
<body>

<p id=“myParagraph”><a href=“http://www.yahoo.com/”>This is my link!</a></p>

<script>

var myLink = document.querySelector(“#myParagraph a”);

myLink.href = “http://www.google.com”;

</script>

</body>

</html>


In this example, we are modifying the href property, changing it from www.yahoo.com to www.google.com. If you load this example into your browser and click the link, it will take you to Google rather than to Yahoo. Using this technique, you can modify most of the simple properties on an element: name, href, even the element’s id.

Modifying Styles

For some properties, the DOM provides a more robust interface. For example, in Listing 10, an element’s style attribute provides a mapping of all of the inline styles on an element.

Listing 10. Modifying an Element’s style Attribute

</p><html>

<head>
<body>

<p id=“myParagraph”><a href=“http://www.yahoo.com/”>This is my link!</a></p>

<script>

var myLink = document.querySelector(“#myParagraph a”);

myLink.style.backgroundColor = “#ff0000”;

myLink.style.color = “#fff”;

</script>

</body>

</html>


In this example, we are modifying the link so that the background color is red and the foreground color is white.

When accessing an element’s style attribute, as shown in Listing 11, all you are doing is working with inline styles. The style attribute is not a representation of styles applied to the element through a style sheet, as demonstrated in Listing 11.
Listing 11. The style Attribute Is Only for Inline Styles

<html>

<head>

<style>

#myParagraph a {

background-color: #ff0000;

color: #ffffff;

}

</style>
<body>

<p id=“myParagraph”><a href=“http://www.yahoo.com/”>This is my link!</a></p>

<script>

var myLink = document.querySelector(“#myParagraph a”);

alert(myLink.style.backgroundColor); // will alert ““ (empty)

alert(myLink.style.color); // will alert ““ (empty)

</script>

</body>

</html>


Both of these alerts will be empty, even though we have set the background color and text color. That’s because they were set using a style sheet rather than an inline style.

Similarly, if you modify an element’s style attribute, the DOM will insert your changes as an inline style. Examining the element in Listing 11, you would see that it now looks something like this:

<a href=“http://www.yahoo.com/” style=“background-color: rgb(255, 0, 0); color: rgb(255, 255, 255);”>This is my link!</a>

You can determine which styles are currently at work on an element by using the DOM’s getComputedStyle() method of the window object, as shown in Listing 12. This method takes an element reference and returns an object representing the styles that are currently active on the element, whether they came from a style sheet or an inline style. The object will be of the same format as the style attribute on the element.

Listing 12. Using the window.getComputedStyle() Method

<html>

<head>

<style>

#myParagraph a { #myParagraph a {

background-color: #ff0000;

color: #ffffff;

}

</style>
<body>

<p id=“myParagraph”><a href=“http://www.yahoo.com/” style=“color: #00ff00”>This is my

link!</a></p>

<script>

var myLink = document.querySelector(“#myParagraph a”),

styleObject = window.getComputedStyle(myLink);

alert(styleObject.backgroundColor); // will alert something like “rgb(255, 0, 0)”

alert(styleObject.color); // will alert something like “rgb(0, 255, 0)”

</script>

</body>

</html>


This example will first alert the color applied to the background of the target element—in this case red, or rgb(255, 0, 0). The second alert will show the color of the text. In this case, we have two conflicting styles, one in the style sheet and an inline style. The inline style has the higher specificity, so it wins, and the script will alert green, or rgb(0, 255, 0).
Another common property to change is an element’s class. The HTML5 DOM specification includes a robust interface for managing classes: the classList property. When accessed directly, the classList property will return an array-like object containing the classes applied to the element. (If no classes are applied to the element, the object will be of length 0.) Each individual class can be accessed via indexes. In addition, the classList property exposes a set of useful helper methods (seen in use in Listing 13):

classList.add(classname): Adds class classname to the classList.

classList.contains(classname): Returns true if classname is present in classList.

classList.remove(classname): Removes class classname from classList.

classList.toggle(classname): If classname is present in classList, it is removed; otherwise it is added.

Listing 13. Using the classList Interface

<html>

<head>

<style>

.redclass {

background-color: #ff0000;

}

.greenclass {

background-color: #00ff00;

}

</style>
<body>

<p id=“myParagraph” class=“redclass”>Here is a paragraph.</p>

<script>

var myPar = document.getElementById(“myParagraph”);

myPar.classList.toggle(“redclass”); // removes redclass from classList

myPar.classList.add(“greenclass”); // adds greenclass to classList

</script>

</body>

</html>


In this example, the background color of the paragraph will be green.

Older versions of browsers will not have the classList interface, and you will instead have to manually modify the class string through the className property. Also, many JavaScript libraries provide methods for managing classes.

Modifying Content

Another common task is accessing and modifying the content of an element. The DOM provides a property to access the actual markup inside an element as well as a property to access just the text contained within that markup:

Node.innerHTML: Provides an interface to the HTML inside of a node. When simply accessed, it returns the HTML contained within the node. If used as a setter, it erases the HTML contained within the element (and its associated nodes in the DOM) and replaces it with the specified HTML (and adds the associated nodes to the DOM). This interface was originally created by the Internet Explorer team many years ago, and it was so incredibly useful that all other browser teams implemented it before it became part of the HTML5 standard.

Node.innerText (nonstandard, available in all browsers except Firefox) or Node.textContent (standard, available in modern browsers except Internet Explorer): Similar to Node.innerHTML except it only returns the text of all the elements contained within the node. It does not return any HTML markup. When used as a setter, it erases all content within the node and inserts the supplied text.

Listing 14 shows examples of both of these in use.

Listing 14. Using innerHTML and innerText

<html>

<head>


<p id=“firstParagraph”>Here is a paragraph.</p>

<p id=“secondParagraph”>Here is another paragraph. It contains some other tags, <a href=“http://www.google.com/”>as well.</a></p>

<script>

var firstPar = document.getElementById(“firstParagraph”),

secondPar = document.getElementById(“secondParagraph”);

alert(firstPar.innerText); // will alert “Here is a paragraph.”

alert(secondPar.innerText); // will alert “Here is another paragraph. It contains some other

tags, as well.”

alert(firstPar.innerHTML); // will alert “Here is a paragraph.”

alert(secondPar.innerHTML); // will alert “Here is another paragraph. It contains some other tags,<a href=“http://www.google.com/”>as well.</a>“

firstPar.innerText = “I have changed the text.”; // will change the text of the first paragraph

secondPar.innerHTML = “<ul><li>How do I love thee?</li><li>Let me count the ways!</li></ul>“;

// will change the HTML inside the second paragraph

</script>

</body>

</html>


In this example, we first use the properties to see the content of the paragraphs, then we use them to change the content.

Caution : These methods are remarkably useful, but carry with them an important caveat:be very careful with innerHTML, as anything thatyou put in there will be parsed into DOM nodes and inserted into the DOM.This can present a serious security problem if you are not carefullysanitizing the HTML you are inserting into the DOM. Specifically, be verycareful of using innerHTML with any content thatcomes from the user, or that you do not have complete control over. Thesemethods will insert any HTML, including script tags, so if you blindly insertuser-provided HTML into your document, it would be trivial for a user toinclude a malicious script that could access your application’s data andcompromise your security completely.
Creating New Elements

In addition to innerHTML, the DOM provides a generic method for creating new elements: the createElement() method. It takes as an argument an HTML tag name, and returns a plain DOM node of the specified type. You can then work with this node as if it were one you had accessed via one of the access methods: you can modify its properties, change its content, and so forth.

The resulting node is not attached to the document, so the DOM also provides the following set of methods for inserting these fragments into the main DOM, thus causing their associated markup to be rendered in the browser window:

parentNode.appendChild(fragment): Appends the DOM fragment as a child of parentNode, at the end of its existing child nodes (if any)

parentNode.insertBefore(fragment, targetNode): Inserts the DOM fragment as a child of parentNode and a sibling of targetNode just before it in the document

parentNode.replaceChild(fragment, targetNode): Replaces targetNode with fragment

Note that the fragment can refer either to a detached fragment created using createElement() (or other methods) or to an existing node within the document. If the latter, these methods will remove the fragment from its previous location before inserting it into its new location. This makes it easy to move nodes from one place to another within the DOM.

Finally, the DOM provides a way to copy existing nodes using the cloneNode() method, seen in Listing 15. The cloneNode() method can take an optional boolean argument that, if set to true, instructs the clone to be “deep” and include all child nodes of the target node.

Listing 15. Using DOM Methods to Create New Nodes and Add Them to the Document

<p id=“firstParagraph”>Here is a paragraph.</p>

<p id=“secondParagraph”>Here is another paragraph. It contains some other tags, <a href=“http://www.google.com/”>as well.</a></p>

<script>

var firstPar = document.getElementById(“firstParagraph”),

secondPar = document.getElementById(“secondParagraph”),

targetLink = document.querySelector(“#secondParagraph a”),

myNewList = document.createElement(“ul”),

myNewListItemTemplate = document.createElement(“li”),

myNewListItem = myNewListItemTemplate.cloneNode();

myNewListItem.classList.add(“menuitem”);

myNewListItem.innerText = “One”;

myNewList.appendChild(myNewListItem);

myNewListItem = myNewListItemTemplate.cloneNode();

myNewListItem.appendChild(targetLink);

myNewList.appendChild(myNewListItem);

firstPar.appendChild(myNewList);

</script>

</body>

</html>


In this example, we start out by creating an unordered list and a list item template using the createElement() method. Then we clone the template, give the clone a new CSS class and some text using the innerText property, and append it to the unordered list. Then we clone the template again and append to it the link from the second paragraph. This removes that link from that location and inserts it into the list item. Finally, we append the list item to the list and append the list to the first paragraph.
Deleting Elements

The DOM gives us a few ways to delete target nodes:

parentNode.removeChild(targetNode): Deletes targetNode from parentNode.

parentNode.innerHTML: By setting the innerHTML of a node to an empty string, we can remove all of its children at once.

There are some caveats when it comes to deleting elements from the DOM, however. If the elements have event handlers attached to them, particularly event handlers that make liberal use of closure to maintain their state, simply removing the elements those handlers are bound to will not necessarily clear them out of memory. This is a prime cause of memory leaks in dynamic applications. Be sure to explicitly remove event handlers from elements (and their children, of course) before removing them from the DOM.

Older versions of Internet Explorer (6 and 7 mostly) are particularly bad about not freeing up memory when elements are removed from the DOM. In fact, these older versions of Internet Explorer hold on to some of the memory for each element even after the elements have been removed from the DOM, resulting in a memory leak. Highly dynamic pages, where lots of elements are added and removed from the DOM, will simply grow larger and larger in memory in IE. There is a simple trick to getting around this: the IE proprietary property outerHTML.

The outerHTML property references the HTML of the parents of the element; when used as a setter and given an empty string, it removes the element from the DOM and efficiently from memory. It doesn’t completely clear the element from memory, but it does help.

So, for an average element deletion, you should follow steps similar to these (as demonstrated in Listing 16):

1. Delete any event handlers from the target element and its children.

2. Delete the element using removeChild().

3. Check if outerHTML is available and, if so, use it to clear the memory in IE.

Listing 16. Efficiently Removing an Element from the DOM and from Memory

var myTarget = document.getElementById(“deleteme”);

deleteme.removeEventListener(“click”, clickHandler, false);

myTarget.parentNode.removeChild(myTarget);

if (typeof document.outerHTML !== “undefined”) {

myTarget.outerHTML = ““;

}


In the contrived example shown in Listing 16, we first get a reference to our target element, then we remove its event handler, and then we remove it from the DOM. Then we check if we are operating in Internet Explorer and, if so, clear the memory associated with the element.

If you’re using a JavaScript library, it will probably manage this process for you, especially if it exposes its own API for deleting elements from the DOM.

DOM Events

In addition to providing access to elements, the DOM specifies a framework for handling user interactions with elements. As the user interacts with the elements on the page—mousing over them, clicking them, selecting them, dragging them, typing within them, and so on—the browser translates those interactions into events within the elements. You can then attach an event handler to an element for a specific event; an event handler is essentially a block of code that the browser will execute when the event happens.

The DOM provides a simple but robust framework for handling events. . . and Internet Explorer ignores it almost completely until IE9. In versions prior to IE9, Internet Explorer used different methods to bind event handlers, provided a different execution context for events, and even didn’t have an entire phase of the event model.

On the positive side, dealing with Internet Explorer’s different event model is a common task, so solutions are plentiful and robust. We will discuss some of these solutions at the end of this section, but for now we will just concentrate on how the event model is designed to work.

Event Phases

When the user interacts with an element, the browser checks whether an event handler is registered for that event type on that element. If so, the browser executes that handler.

Because HTML is structural and tags can be nested, the DOM specifies that an event that starts in one element will “bubble up” through its parent elements—after all, an event in a child element might need to be counted in a parent element as well. After an event has occurred on a target element, the browser will then “bubble up” the event to the target element’s parent. It will then perform the same check to determine if a handler is registered for the event type and execute it if there is. Then it will bubble up to the next parent, and so on. Eventually the event will reach the trunk of the DOM tree—the body element, which is the ultimate grandparent of any element in the document. At that point, the event will traverse back down the path it just followed to the original target, again checking for and executing registered event handlers at each element, all the way back to the original target element. Once the round trip is completed, the event terminates.

The phase when the event is moving up the DOM tree is called the bubbling phase, and the phase when the event is going back down the DOM tree is called the capturing phase. You can specify which phase you want your event handler to execute in, giving you great flexibility in handling events on nested elements. For example, if you want to have a click event execute on a parent element before it executes on the original child target, you can register the parent event handler in the bubble phase and register the child event handler in the capture phase.

There is a third phase for events, called at target, which is when the event is currently at the target element. There is no way to target this phase directly when binding event handlers, but you can access it via a property in the Event object (see “The Event Object,” below).
Event Execution Context

When the browser executes an event handler, it has to provide an execution context for that function. The DOM standard specifies that this context should be the element that the event handler is bound to. So, within an event handler function, the this keyword will be a pointer to the DOM element that the event is executing on.

Also, when an event handler is executed, the browser will pass into it (as a parameter) an Event object that contains several useful properties that provide details about the event: the original target element where the event originated, the mouse location within the target, the mouse location within the page, and so on. In addition, the Event object has some useful methods for modifying the event’s propagation behavior (we will cover these in the section titled “The Event Object,” below).

Different Events

The DOM standard provides a huge number of events. They can be grouped into six basic groups:

Mouse events: click, mousedown, mouseup, mousemove, etc. These are covered by the DOM MouseEvents module.

Keyboard events: keypress, keydown, and keyup. Covered by the DOM KeyboardEvents module.

Object events: load, error, resize, scroll, etc. Covered by the DOM HTMLEvents module.

Form events: select, change, submit, reset, focus, etc. Also covered by the DOM HTMLEvents module.

User Interface events: focusin and focusout. Covered by the DOM UIEvents module.

Mutation events: Events that are fired as things change within the DOM, such as DOMNodeInserted, DOMAttrModified, etc. Covered by the DOM MutationEvents module.

In addition, browsers on mobile devices might also expose other events related to touch interactions (tap, doubletap, taphold, swipe, etc.) or other occurrences unique to mobile devices (orientation changes, shaking, movement, location, etc.).

Binding Event Handlers

In order for a browser to react to events on an element, you must first bind a handler for that event to the element. Binding an event handler is essentially the same as saying, “When the user interacts with this element in this way, execute this code when the event passes through this phase.” The method the DOM provides for that is the addEventListener() method, which takes three arguments: an event type argument (click, keypress, etc.), a listener argument (the code to execute when the event happens), and an optional boolean phase parameter that instructs the event handler to execute either during the capture phase (if set to true) or during the bubbling phase (if set to false, which is the default). (In some older browser versions, the boolean for capture wasn’t always optional, so it’s considered good practice to always include it.) The event handler can take an event object as a parameter, and you can then access the event object within the handler. You can see an example of binding a click event handler in Listing 17.

Listing 17. Binding a Click Event Handler to an Element

<html>

<head>


<p id=“firstParagraph”>Click Me!</p>

<script>

var firstPar = document.getElementById(“firstParagraph”);

function myEventHandler(event) {

alert(“You clicked me!”);

}

firstPar.addEventListener(“click”, myEventHandler, false);

</script>

</body>

</html>


When you click the paragraph, the alert box will open.

You can add more than one event handler to an event on a single object:

firstPar.addEventListener(“click”, myFirstEventHandler, false);

firstPar.addEventListener(“click”, mySecondEventHandler, false);


The event handlers will execute in the order of binding when the event is triggered.

That’s the basic pattern for binding event handlers to elements. You can use an inline anonymous function instead of a named function if you prefer, as shown in Listing 18, which is also a fairly common pattern.

Listing 18. Using an Anonymous Inline Function as an Event Handler

<html>

<head>
<body>

<p id=“firstParagraph”>Click Me!</p>

<script>

var firstPar = document.getElementById(“firstParagraph”);

firstPar.addEventListener(“click”, function(event) {

alert(“You clicked me!”);

}, false);

</script>

</body>

</html>


This example will behave exactly the same as the example in Listing 17. The difference is only in the named function. Note that if you are going to have a complex event handler, it’s probably worth the effort to create a named function for it and pass it as a parameter. If you have an inline event handler that becomes too long (especially if it gets long enough to go across more than one screen), your code can be confusing to read.
Unbinding Event Handlers

To unbind an event handler, use the removeEventListener() method, as shown in Listing 19. Just like addEventListener(), removeEventListener() takes three parameters: an event type, the handler function to remove (in the case of named functions), and the boolean phase parameter.

Listing 19. Removing an Event Handler

<html>

<head>
<body>

<p id=“firstParagraph”>Click Me!</p>

<script>

var firstPar = document.getElementById(“firstParagraph”);

function eventHandler(event) {

alert(“I’m unbinding the event handler!”);

firstPar.removeEventListener(“click”, eventHandler, false);

}

firstPar.addEventListener(“click”, eventHandler, false);

</script>

</body>

</html>


In this example, when you click the paragraph, it will execute the handler, which will then unbind itself. Note also that this example uses a closure: both the firstPar variable and the eventHandler function remain available even after the event handler has been bound and the script has completed execution. That way, when the event handler is called by an event on the target object, it will be able to successfully execute. Maintaining state for event handlers is one of the more common uses of closures in JavaScript development. (See Chapter 1 for more details about closures.)

If you call removeEventListener() with a combination of parameters that doesn’t match any event handlers that have been added to the object, the method simply terminates. It does not throw an error or give any indication that it had no effect.

There is no way to unbind an event handler that uses an anonymous inline function. You need to be able to refer to a function name for removeEventListener().

The Event Object

The Event object is passed into the event handler, so if you want to, you can access it within your scripts. The Event object has several useful properties and methods, most notably:

event.clientX, event.clientY: The mouse coordinates of the event relative to the browser window (if a mouse event).

event.offsetX, event.offsetY: The mouse coordinates of the event relative to the target element (if a mouse event).

event.keyCode: The ASCII code of the key that was pressed (if a keyboard event).

event.target: A pointer to the DOM element where the event originated.

event.currentTarget: A pointer to the DOM element where the event is currently bubbled (or captured) to. For example, if you had an unordered list consisting of a UL tag containing LI tags, when you click an LI tag, the click event will bubble up to the parent UL tag, then up to the parent of the UL tag, and so on. As the event bubbles up, the currentTarget property will change value to reflect the location of the event in the bubbling process.

event.eventPhase: An integer code indicating which phase the event is currently in: 1 for capture, 2 for at target, 3 for bubbling.

event.type: The type of the event (“click”, “keypress”, etc.).

event.relatedTarget: Used in some specific events (such as mouseout) to point to the element where the event originated (or that received the event, in the case of mouseout).

event.stopPropagation(): When this method is called, it stops the event from propagating any further through the DOM. If more than one event handler is registered to this element for this event, however, any remaining event handlers will still execute.

event.stopImmediatePropagation(): Like stopPropagation(), but when this method is called, it will also stop any remaining event handlers on the current element from executing as well as preventing any further propagation.

event.preventDefault(): If there is a default action associated with the event, calling this method will prevent it from executing. For example, if you registered a click event handler to an anchor tag, calling preventDefault() within it would prevent the browser from following the link. In Internet Explorer, this method is not present. Instead, it is replaced with a boolean property returnValue, which, when set to false, will cancel the default action.

These properties tell us a lot about the event and give us a lot of flexibility in writing our event handlers. To illustrate, Listing 20 offers a simple game of Kitten Rescue.

Listing 20. Rescue the Kittens!

<html>

<head>

<style>

.basket {

width: 300px;

width: 300px;

height: 300px;

position: absolute;

top: 100px;

right: 100px;

border: 3px double #000000;

border-radius: 10px;

}

</style>
<body>

<h3>Rescue the kittens!</h3>

<p>Click on them to put them in their basket!</p>

<ul id=“kittens”>

<li>Rowly</li>

<li>Fred</li>

<li>Mittens</li>

<li>Lenore</li>

</ul>

<script>

var basket = document.querySelector(“.basket”),

kittens = document.querySelectorAll(“li”),

kittensLength = kittens.length,

i;

for(i = 0; i < kittensLength; i++) {

kittens[i].addEventListener(“click”, function(event) {

basket.appendChild(event.target);

}, false);

}

</script>

</body>

</html>
In this example, we are registering a click event handler to each kitten, so that when you click on a kitten, it is magically transported to the safety of its basket (or, in our case, we simply append it to the target DOM node, which automatically removes it from its original location in the DOM).

In this game, there’s a bit of an inefficiency: we assign an event handler to each item separately. We don’t actually have to do this; if we want to, we can take advantage of the fact that events bubble up through the DOM using a method called event delegation.

Event Delegation

A common pattern you’ll see in event handling is event delegation. Basically, delegating an event means allowing the event to be handled by an element that is higher up in the DOM tree than the original target. This can reduce the number of event handlers you have to employ, and thus can have a significant effect on efficiency.

As an example, let’s redo our game using event delegation. Instead of applying a separate event handler to each kitten, let’s delegate the event handler to the containing element, as shown in Listing 21.

Listing 21. Kitten Rescue, Event Delegation Version

<!DOCTYPE html>

<html>

<head>

<style>

.basket {

width: 300px;

height: 300px;

position: absolute;

top: 100px; top: 100px;

right: 100px;

border: 3px double #000000;

border-radius: 10px;

}

</style>


<h3>Rescue the kittens!</h3>

<p>Click on them to put them in their basket!</p>

<ul id=“kittens”>

<li>Rowly</li>

<li>Fred</li>

<li>Mittens</li>

<li>Lenore</li>

</ul>

<script>

var basket = document.querySelector(“.basket”),

kittens = document.getElementById(“kittens”);

kittens.addEventListener(“click”, function(event) {

basket.appendChild(event.target);

}, false);

</script>

</body>

</html>


In this example, we let the containing unordered list element handle the click events. Now we have only one event handler, and our code is that much simpler.

Manually Firing Events

The DOM also enables you to trigger events manually. When you manually trigger an event in your code, it behaves exactly as an event that was dispatched by a user.

Manually triggering an event involves three steps:

1. Create an event object of the appropriate type.

2. Configure the object appropriately. The DOM provides several methods for correctly initializing event objects so that they have all the parameters necessary. The DOM also provides a simpler method for initializing event objects for situations where you only need a minimum of information for your event.

3. Dispatch the event on the element.

Let’s look at each of those steps in detail.
NEWMANUAL EVENTS ON THE HORIZON

The latest version of the HTML5 specification proposes a new way of modeling events using a global Event object. In this proposal, this Event object can act as a constructor just like Object or Array, and you can create and configure your event objects that way. Although this is not yet approved, it is already being implemented in some browsers. For details on this upcoming functionality, see the “Interface CustomEvent” section in the W3C DOM4 standard, at https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-customevent.

Creating an Event Object

To create an event object, you use the document.createEvent() method. This method takes a single parameter, which is a string representing the DOM event module you are going to be using. The following are the most commonly used modules:

MouseEvents: Events dealing with mouse interactions, such as click, mousedown, mousemove, etc.

UIEvents: Used for focus events, which occur when elements are focused and are receiving keyboard input, as in the case of form fields or content editing.

HTMLEvents: Used for browser-oriented events such as document loading and unloading, as well as content selection, resizing, and scrolling.

MutationEvents: Events dealing with changes to the DOM, such as DOMNodeInserted, DOMAttrModified, etc.

KeyboardEvents: Events dealing with keypresses: keyup, keydown, and keypress.

Event: This is a generic event module that can be used to send any event.

Once you have an event object of the appropriate type, you can configure it as needed.

Configuring an Event Object

The DOM provides convenience methods to help you appropriately configure your shiny new event object. Which method you should use depends on which module your event is a member of.

Events in the MouseEvents module use Event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget), where the properties are as follows:

type: The actual event type, such as click, mousedown, etc.

canBubble: A boolean indicating whether or not the event should bubble up through the DOM.

cancelable: A boolean indicating whether or not the event’s default action can be canceled using event.preventDefault.

view: The event’s meta context, which in JavaScript is always the global context, so always pass a reference to the window object here.

detail: Specific detail about the event. For MouseEvents, it is the number of mouse clicks in the same location (thus, if detail = 2, it was a double-click event).

screenX and screenY: The x and y coordinates relative to the body of the event.

clientX and clientY: The x and y coordinates relative to the target element of the event.

ctrlKey, altKey, shiftKey, metaKey (Mac OS X): Booleans indicating whether or not those keys were pressed at the time of the event.

button: A number indicating which button was clicked: 0 indicates a left click, 1 indicates a middle button (usually the mouse wheel on modern mice), and 2 indicates a right-click.

relatedTarget: The related target for the event, if appropriate.

Events of the UIEvents module use Event.initUIEvent(type, canBubble, cancelable, view, detail), where:

type: The actual event type.

canBubble: A boolean indicating whether or not the event should bubble up through the DOM.

cancelable: A boolean indicating whether or not the event’s default action can be canceled using event.preventDefault.

view: The event’s meta context, which in JavaScript is always the global context, so always pass a reference to the window object here.

detail: Specific detail about the event. For UIEvents, it’s usually the number of times the mouse was clicked as part of the event, so this is often set to 1.

Events of the HTMLEvents module use Event.initEvent(type, canBubble, cancelable), where:

type: The actual event type.

canBubble: A boolean indicating whether or not the event should bubble up through the DOM.

cancelable: A boolean indicating whether or not the event’s default action can be canceled using event.preventDefault.
Events of the MutationEvents module use Event.initMutationEvent(type, canBubble, cancelable, relatedTarget, previousValue, newValue, attributeName, attributeChange), where:

type: The actual event type.

canBubble: A boolean indicating whether or not the event should bubble up through the DOM.

cancelable: A boolean indicating whether or not the event’s default action can be canceled using event.preventDefault.

relatedTarget: The related target for the event, if appropriate.

previousValue: The previous value of the modified node.

newValue: The new value of the modified node.

attributeName: The name of the modified attribute.

attributeChange: An integer indicating how the attribute was changed: 1 = modification, 2 = addition, 3 = removal.

When it comes to initializing a KeyboardEvents event, you use Event.initKeyboardEvent(type, canBubble, cancelable, view, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode), where:

type: The actual event type.

canBubble: A boolean indicating whether or not the event should bubble up through the DOM.

cancelable: A boolean indicating whether or not the event’s default action can be canceled using event.preventDefault.

view: The event’s meta context, which in JavaScript is always the global context, so always pass a reference to the window object here.

ctrlKey, altKey, shiftKey, metaKey (Mac OS X): Booleans indicating whether or not the virtual keypress happened while these keys were pressed.

keyCode: The ASCII code of the key.

charCode: The Unicode character of the key.

Note that the nonstandard browser here is Firefox, which instead calls the method initKeyEvent(). Originally the KeybordEvent module was defined in early versions of the DOM Level 2 specification, but it was removed from that specification.

EXTENDING EVENT OBJECTS

Feel free to extend these event objects ifyou want. They’re objects, just like any other objects in JavaScript, so youcan add your own properties and methods to them. One of our favoritetechniques is to provide an appDetail property tothe events, which contains useful information about the event and why it wastriggered. This also makes it easy to determine which events were manuallytriggered and which events were triggered directly by users. It also worksgreat with custom events (described a bit later in the chapter).

Now that you have a configured event, you just have to dispatch it.

Dispatching an Event

Dispatching your event is quite simple. As shown in Listing 22, you call the dispatchEvent() method on the target element.

Listing 22. Firing a Custom Event

<html>

<head>
<body>

<p id=“clickme”>Click me to see an alert!</p>

<script>

var myPar = document.getElementById(“clickme”);

myPar.addEventListener(“click”, function(event) {

alert(‘This is your alert!’);

}, false);

// Create and dispatch a new click event

var myClickEvent = document.createEvent(“MouseEvents”);

myClickEvent.initMouseEvent(“click”, true, true, window, 0, 0, 0, 0, false, false, false, false,

1, null);

myPar.dispatchEvent(myClickEvent);

</script>

</body>

</html>


In this example, we are manually firing a click event, so that when you load this page, you’ll immediately see an alert as if you had clicked the paragraph. You can click the paragraph to see the alert again.
In Listing 22, though, we don’t actually need all of those parameters—we set the coordinates to 0 (even though that’s not at all accurate), we don’t really care about the Cntrl, Shift, or meta keys, and so forth. If you won’t be needing all of those properties, you can use the simpler event object generated by the generic Events module. Consider, for example, Listing 23, which automates our Kitten Rescue game—because nothing’s more fun than making a game that finishes itself.

Listing 23. Automating Kitten Rescue

<html>

<head>

<style>

.basket {

width: 300px;

height: 300px;

position: absolute;

top: 100px;

right: 100px;

border: 3px double #000000;

border-radius: 10px;

}

</style>
<body>

<h3>Rescue the kittens!</h3>

<p>Click on them to put them in their basket!</p>

<ul id=“kittens”>

<li>Rowly</li>

<li>Fred</li>

<li>Mittens</li>

<i>Lenore</li>

</ul>

<script>

var basket = document.querySelector(“.basket”),

kittens = document.getElementById(“kittens”);

kittens.addEventListener(“click”, function(event) {

basket.appendChild(event.target);

}, false);

// Make JavaScript rescue the kittens!

var allKittens = document.querySelectorAll(“#kittens li”),

allKittensLength = allKittens.length,

i,

clickKittenEvent = document.createEvent(“Event”);

clickKittenEvent.initEvent(“click”, true, true);

for (i = 0; i < allKittensLength; i++) {

allKittens[i].dispatchEvent(clickKittenEvent);

}

</script>

</body>

</html>
In Listing 23, we are looping through each kitten and firing a minimally configured click event on them, which we created using the generic Events module. This provides a quick way of firing events, should you need it.

Note, however, that some events require more complex event objects. Keyboard events in particular seem to be sensitive to this. You might need to experiment to find out what you can use.

Custom Events

You can use the DOM event model to dispatch any kind of event you want! Yes, you read that right: you aren’t limited to clicks and keypresses. If you want to define your own events, you can do that. Just specify your event type when you use the addEventListener() method to attach the event handler, and then use the generic Events module to create and fire your own events.

Consider our Kitten Rescue game. Imagine that instead of listening for a click event, we listen for a “rescue” event, as shown in Listing 24. We can then manually generate rescue events and rescue all the kittens.

Listing 24. Custom Events to the Rescue

<html>

<head>

<style>

.basket {

width: 300px;

height: 300px;

position: absolute;

top: 100px;

right: 100px;

border: 3px double #000000;

border-radius: 10px;

}

</style>
<body>

<h3>Rescue the kittens!</h3>

<p>Click on them to put them in their basket!</p>

<ul id=“kittens”>

<li>Rowly</li>

<li>Fred</li>

<li>Mittens</li>

<li>Lenore</li>

</ul>

<script>

var basket = document.querySelector(“.basket”),

kittens = document.getElementById(“kittens”);

kittens.addEventListener(“rescue”, function(event) {

basket.appendChild(event.target);

}, false);

// Make JavaScript rescue the kittens!

var allKittens = document.querySelectorAll(“#kittens li”),

allKittensLength = allKittens.length,

i,

clickKittenEvent = document.createEvent(“Event”);

clickKittenEvent.initEvent(“rescue”, true, true);

for (i = 0; i < allKittensLength; i++) {

allKittens[i].dispatchEvent(clickKittenEvent);

}

</script>

</body>

</html>


In this example, we just change the click event handler to a rescue event handler, and then we simply create a rescue event and dispatch it from each kitten, just like we did with click events.

Creating custom events is a powerful technique that allows you to create decoupled components in your code. Each component only needs to publish events as things happen to it, and then other components can listen for those events, or not. That way, all components are completely decoupled: components A, B, and C do not need to know anything about one another, or even if they exist or not, but they can still communicate with one another using events.
Cross-Browser Strategies

As we mentioned at the beginning of this section, Internet Explorer pretty much ignores the standard for DOM events up until version 9. Most notably, instead of addEventListener() and removeEventListener(), Internet Explorer uses the methods attachEvent() and removeEvent(). In addition, Internet Explorer doesn’t set the proper context for the executing event handler; instead of setting it to the element where the handler was registered, IE sets it to the window object. IE also doesn’t support the capture phase for events. And finally, IE doesn’t pass an Event object into its event handlers; instead, it tacks it on to the window object.

Fortunately, most of these problems are easy to mitigate with a bit of JavaScript. The lack of a capture phase is difficult to overcome, but the capture phase is not widely used, so if we focus on the registration methods and context problems, we can come up with a fairly simple solution.

What we’ll do is create two new functions that we’ll use to register our events: addEventHandler() and removeEventHandler(), as shown in Listing 25. If we’re working in a browser that supports the DOM standard, we’ll just alias our functions to the DOM functions and leave it at that. If we’re in IE, though, we’ll need to do a bit more to fix our context problems.

Listing 25. Creating Cross-Browser Event Binding Methods

if (document.addEventListener) {

// DOM events available, so just use them.

window.addEventHandler = function(targetEl, eventType, handler) {

targetEl.addEventListener(eventType, handler, false);

return handler;

};

window.removeEventHandler = function(targetEl, eventType, handler) {

targetEl.removeEventListener(eventType, handler, false);

}

} else {

// Internet Explorer. Fix context problems as well as create alias.

window.addEventHandler = function(targetEl, eventType, handler) {

var fixContext = function() {

return handler.apply(targetEl, arguments);

};

targetEl.attachEvent(“on” + eventType, fixContext);

return fixContext;

}

window.removeEventHandler = function(targetEl, eventType, handler) {

targetEl.detachEvent(“on” + eventType, handler);

}

}


In this example we add two new methods to the global context: addEventHandler() and removeEventHandler(). In IE, for addEventHandler(), we fix our context problem by creating a dummy function called fixContext() and binding that as the event handler. When fixContext() is called by the event, it manually invokes the handler using the apply() method, which enables us to force the target element to be the execution context.

We also mentioned that Internet Explorer doesn’t pass in an Event object as a parameter to its event handlers. There’s a simple way around that, too, as shown in Listing 26: within the event handlers, just check to see if there was an event passed in and, if not, pull it from the window object (which is where IE puts it).

Listing 26. Fixing the Other IE Problem

function clickHandler(event) {

if(!event) {

event = window.event;

}

// continue...

}


Addressing the lack of a capture phase is more problematic, but these fixes take care of the worst of the problems. If you find you need a more robust solution, many JavaScript libraries handle this problem very well. jQuery in particular fixes all the problems, as well as providing many useful extensions to the event model.
Summary

In this chapter we have covered an important feature in browsers, the DOM, which provides JavaScript with an interface for accessing and manipulating the document that has been loaded into the browser. Here are the important points to take away from the chapter:

The DOM is a separate standard; it is not governed by the JavaScript standard.

The DOM has evolved over time, and browser compliance is an ongoing process.

The DOM is structured like a tree, and everything that is in the HTML document is represented in that tree.

The DOM can be traversed using parent/child/sibling relationships and convenience methods.

Nodes in the DOM can be accessed directly using methods like getElementById() and querySelector().

The DOM provides several important methods for manipulating its members, including ways to change their properties and their content.

The DOM allows you to create nodes as needed, and work with them as if they were taken from a document.

The DOM has a rich and flexible event model. . . which Internet Explorer doesn’t follow.

Event handlers can be added to any element, and removed just as easily.

Event handling can be delegated to elements higher in the DOM because events bubble up through the DOM structure, eventually reaching the body tag.

Events can be manually fired.

You can create custom events.

This chapter marks the end of the discussion chapters. In Chapter 4 we’ll have some fun applying everything we’ve covered in the first three chapters in some practical projects.