Chapter 5. Manipulating the DOM

Having an efficient method to send a request to the server and pull back its response without having to refresh the whole page is very important. It is only a small part of Ajax development, though. What is more important to any Ajax web application is what is done with the data the client receives. There’s a lot more work to do than just grabbing the data, formatting it, and setting it equal to the innerHTML of an element. Understanding how the HTML Document Object Model (DOM) works and how to manipulate it is of utmost importance. I like to think that this—manipulating the DOM—is where the magic of Ajax actually happens. This is what gives Ajax life and allows application development on the Web.

The first key to understanding how the DOM works is to examine the structure of a DOM object. Then it will become clearer how the methods allow you to manipulate the DOM.

Understanding the DOM

The structure of any DOM object is its document tree. The document tree is made up of branches and leaves. Let’s look at a simple XHTML document, shown in Example 5-1, to clarify.

Example 5-1. A simple XHTML document

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <title>A Document tree example</title>
    </head>
    <body>
        <div id="body_content">
            <h1>A Document tree example</h1>
            <p>
                This is just a <em>very</em> simple example.
            </p>
        </div>
        <div id="body_footer">
            This is a simple <strong>footer</strong>.
        </div>
    </body>
</html>

Figure 5-1 shows this file as a simple document tree, with an emphasis on simple.

A simple document tree

Figure 5-1. A simple document tree

I ignored the attributes where they would have been in the document tree to keep this example simpler. The first thing to notice is that the DOCTYPE declaration is not part of the document tree—DOCTYPEs and XML prologs are never part of the tree.

The first element of the tree, <html>, is known as the tree’s root element or root node. All other elements of the tree branch off from this first element. Any elements that branch from the root element are known as the element’s children. These children can be either branches themselves or simply leaves, meaning that they have no children of their own. The <title> element is a child of the <head> element, and is itself a branch to the content contained within the element. This content would be a leaf on the tree.

As I just said, the content contained within the <title> element is also an element. Specifically, it is a text element or text node. The World Wide Web Consortium (W3C) has standardized the list of node types that any element of a document tree can be, as shown in Table 5-1.

Table 5-1. W3C node types

Node type

Numeric type value

Description

Element

1

Represents an element.

Attribute

2

Represents an attribute.

Text

3

Represents character data in an element or attribute.

CDATA section

4

Represents text that may contain characters that would otherwise be considered markup.

Entity reference

5

Represents an entity reference.

Entity

6

Represents an entity.

Processing instruction

7

Represents a processing instruction.

Comment

8

Represents a comment.

Document

9

Represents the document (this is the root node of the tree).

Document type

10

Represents a list of entities that are defined for this document.

Document fragment

11

Represents a document that is “lighter” than a true document node, as it contains only a part of a document.

Notation

12

Represents a notation declared in the document type definition (DTD).

That is a simple introduction to the structure of a DOM object. Now we need to learn how to traverse the branches of a document tree so that we can manipulate all of the different elements it contains.

We’ve Already Met

The methods that most greatly facilitate DOM Document object traversal might seem a little familiar, as you already met them in Chapter 4. These are getElementById( ) and getElementsByTagName( ). Add to these the Prototype library’s helper functions, and we have a good foundation for accessing specific elements on a document tree.

Just to refresh, here are some common ways to access specific elements:

/* Use Prototype's $( ) function to get an element by its id */
var myElement = $('myElement'),

/* Get an array of elements based on their tag name */
var myElements = exampleDoc.getElementsByTagName('myTag'),

/* Get an array of elements based on their class name */
var myElements = document.getElementsByClassName('myClass'),

/* Get an array of link elements based on their class name */
var myElements = $$('a.myClass'),

These methods and functions make Ajax development easy when we know the id, the class name, and so forth that we are looking for. But what if our Ajax web application is more complicated than that and it requires more sophisticated manipulation? It turns out that a host of methods are available for any kind of DOM manipulation you require.

Manipulating DOM Elements, Attributes, and Objects

Elements are the containers of all the data to be dynamically altered in an Ajax application. They can contain other elements, which contain still others, or they can simply hold a text node with data for the client. When we talk about these elements, we also want to discuss groups of them represented in document fragment objects. To round out this discussion on elements and objects, we will also consider text elements, since the value of these elements is the data in the application.

Our discussion cannot center on just XHTML, either. You could need to alter XML received from a server response just as often as you need to alter the client’s page DOM. We will follow the W3C’s DOM Level 2 Recommendation (the standard methods that are available to a developer from the browser) when discussing methods available to a DOM Document object unless I specify otherwise. This allows you to write more robust code utilizing the power of the DOM, instead of writing workarounds for functionality that may be needed in only a particular area.

Creating Elements, Attributes, and Objects

An important benefit of dynamic content is the ability to create new content from freshly received data. This is necessary in dynamic menu creation, navigation, breadcrumbs, and web services, among other applications. Ajax relies on content changing within the page without having to reload the entire page. To accomplish this, we need to create new parts of the DOM.

The first method we will concentrate on is createElement( ), which is used to create a new Element node. An example of this method is:

var element = document.createElement('div'),

alert(element.nodeName); /* Alerts 'DIV' */

createElement( ) takes as a parameter the name of the element type to instantiate, and creates an element of that specified type. It returns an instance of an Element interface. This is useful as it allows attributes to be directly specified on the returned element node. In this case, we created a new <div> element and used the variable element to store that interface.

You didn’t think creating elements would be any more complicated than that, did you? Now, what if you need to add text data to the DOM Document object? The method createTextNode( ) will do the trick. To create a Text node, you do the following:

var element = document.createTextNode('Text to create.'),

alert(element.nodeValue); /* Alerts 'Text to create.' */

The parameter that createTextNode( ) takes is the data string that you want the node to represent. It then returns a new text node stored in element.

Creating a new attribute for a node may be something your application requires. The createAttribute( ) method takes the name of the attribute as a string parameter, and then creates an Attr node of the passed name, as shown in the following:

var element = $('elem'),
var attribute = document.createAttribute('special'),

attribute.value = 'temp';
element.setAttributeNode(attribute);
alert(element.getAttribute('special')); /* Alerts 'temp' */

The Attr instance that is created can then be set on an Element using the setAttributeNode( ) method. We will discuss this method in the next section, “Modifying and Removing Elements, Attributes, and Objects.”

Adding new elements to a DOM document tree that’s smaller than the page’s document tree can greatly speed up a script if the page is particularly large or complicated. This is where creating a document fragment can come in handy. Creating a new document fragment is as simple as:

var fragment = document.createDocumentFragment( );
var titleText = $('title').firstChild;

fragment.appendChild(document.createtextNode(titleText);
alert(fragment.firstChild.nodeValue); /* alerts /titleText/ */

The createDocumentFragment( ) method does not take any parameters, and it creates an empty DocumentFragment object to which new elements may be added.

Many other methods operate in a fashion similar to the methods I just illustrated. Table 5-2 lists all the DOM Document object methods used to create nodes in a document tree.

Table 5-2. Creation methods

Method

Description

W3C standard

createAttribute(attrName)

Creates a new Attr node having the name set to the passed attrName.

Yes

createAttributeNS(nsURI, qualName)

Creates a new Attr node having the namespace URI set to the passed nsURI and the qualified name set to the passed qualName.

Yes

createCDATASection(textData)

Creates a new CDATASection node with the value set to the passed textData.

Yes

createComment(textData)

Creates a Comment node with the data set to the passed textData.

Yes

createDocumentFragment( )

Creates a new empty DocumentFragment object

Yes

createElement(elemName)

Creates a new Element node with the name set to the passed elemName.

Yes

createElementNS(nsURI, qualName)

Creates a new Element node having the namespace URI set to the passed nsURI and the qualified name set to the passed qualName.

Yes

createEntityReference(refName)

Creates a new EntityReference object with the reference set to the passed refName.

Yes

createNode(nodeType, nodeName, nsURI)

Creates a new node of the passed nodeType, with the name set to the passed nodeName and the namespace URI set to the passed nsURI (this is a Microsoft-specific method).

No

createProcessingInstruction(targ, data)

Creates a new ProcessingInstruction object having the target set to the passed targ and the data set to the passed data.

Yes

createTextNode(textData)

Creates a new Text node having the data set to the passed textData.

Yes

Modifying and Removing Elements, Attributes, and Objects

Being able to create new elements and objects does not do us much good if we have no way to get these new nodes into part of a larger DOM document tree, whether it is a DocumentFragment or a Document. So, in this section we will discuss some methods for appending, removing, and modifying elements, attributes, and objects in a DOM document tree.

One of the most common methods used is appendChild( ). It takes a passed node or object, and adds it to the end of the list of children for the node for which the method was called. For example:

$('title').appendChild(document.createTextNode('This is an appended text node'));

If the passed node is already part of the tree, it is first removed from the tree and then appended to the end of the list. Also remember that if the passed object is a DocumentFragment object, the entire contents of the fragment are appended to the end of the list of children.

If the node that needs to be appended to the calling node should not go to the end of the list of children, you use the insertBefore( ) method to specify a location. For example:

var element = document.createElement('div'),

element.appendChild(document.createTextNode('Some text here.'));
$('subHeading').insertBefore(element, $('bodyText'));

As with the method appendChild( ), if the passed node is already part of the tree, it is first removed and then inserted before the reference node. Figure 5-2 shows what this would look like before the call to insertBefore( ), and Figure 5-3 shows what it would look like after. Also like appendChild( ), when the passed object is a DocumentFragment object, its children are inserted in the order in which they appear in the fragment and before the reference node. When no reference node is supplied, the passed node is inserted at the end of the list of child nodes.

The document before any node insertion

Figure 5-2. The document before any node insertion

The document after the new node is inserted using insertBefore( )

Figure 5-3. The document after the new node is inserted using insertBefore( )

Sometimes nodes need to be removed from the document tree. These cases call for the removeChild( ) method. Here’s an example:

document.removeChild($('loading'));

removeChild( ) takes the node to be removed from the tree as the parameter, and the method returns the removed node after it has been removed from the tree.

At times, you will have built a DocumentFragment that contains a formatted structure from an Ajax feed, and you will need to insert the fragment into the DOM document. The method importNode( ) handles these situations. For example:

var response = results.responseXML;

response = document.importNode(response.documentElement, true);
$('responseDiv').appendChild(response);

When it comes to appending, removing, or modifying data, many methods are available. It would be impractical to demonstrate each of them. So instead, I list and describe them in Table 5-3.

Table 5-3. Manipulation methods

Method

Description

Available interfaces

appendChild(newNode)

Appends the node newNode to the end of the list of children of this node. If the newNode already exists in the tree, it is removed first. If newNode is a DocumentFragment, the contents of the entire fragment are appended to the list.

All

appendData(newData)

Appends the newData string to the end of the character data of the node.

CDATASection, Comment, Text

cloneNode(recursive)

Returns a clone of this node with the exception being that the cloned node has no parentNode. If recursive is true, the method also clones any children of the node; otherwise, it clones only the node itself.

All

deleteData(offset, count)

Deletes data from the node in 16-bit increments, starting at the offset and deleting count * 16-bit increments. If the sum of offset and count is greater than the length of the data, all data from the offset is deleted.

CDATASection, Comment, Text

importNode(node, recursive)

Imports a node from another Document to this Document. The source node is not altered or moved from the original document. Instead, a copy of the node is made. If recursive is true, the method also imports any children of the node; otherwise, it imports only the node itself.

Document

insertBefore(newNode, refNode)

Inserts newNode before the existing refNode. If refNode is null, newNode is inserted at the end of the list of children.

All

insertData(offset, arg)

Inserts arg at the specified 16-bit offset.

CDATASection, Comment, Text

normalize( )

Pulls all Text nodes in the whole document tree, including Attr nodes, and puts them in a form where only structure separates the nodes.

All

removeAttribute(attrName)

Removes the attribute with the name equal to the passed parameter attrName.

Element

removeAttributeNode(attrName)

Removes the Attr node with the name equal to the passed parameter attrName.

Element

removeAttributeNS(nsURI, localName)

Removes the attribute with the namespace URI equal to the passed parameter nsURI and the local name equal to the passed parameter localName.

Element

removeChild(nodeName)

Removes the child node with the name equal to the passed parameter nodeName from the list of children and returns it.

All

replaceChild(newNode, oldNode)

Replaces the child node oldNode with newNode in the list of children, and returns the oldNode.

All

replaceData(offset, count, arg)

Replaces the data starting at the 16-bit offset, replacing a length of count * 16-bits with the passed arg. If the sum of offset and count is greater than the length, all data from the offset to the end of the data is replaced.

CDATASection, Comment, Text

setAttribute(attrName, value)

Creates or alters the attribute with the passed attrName with the value of the passed value.

Element

setAttributeNode(newAttr)

Adds the newAttr node to the attribute list. If newAttr replaces an existing Attr, the replaced node is returned.

Element

setAttributeNodeNS(newAttr)

Adds the newAttr node to the attribute list. If newAttr replaces an existing Attr with the same namespace URI and local name, the replaced node is returned.

Element

setAttributeNS(nsURI, qualName, value)

Creates or alters the attribute with the namespace URI equal to the passed nsURI and the qualified name equal to the passed qualName with the value of the passed value

Element

splitText(offset)

Splits the node into two nodes at the passed offset, keeping both nodes in the Document tree as siblings

CDATASection

Tip

You will notice all of the references to 16-bit units when talking about character data. This is because XML supports Unicode characters, which are two bytes (16 bits) per character.

Element, Attribute, and Object Information

Now that it is clear how to create elements, attributes, and objects and how to modify and remove them in the DOM document tree, you need to know how to access the data. And you have probably already seen some, if not most, of the methods that get information from the elements, attributes, and objects within the document tree.

These methods are often used together to get information from elements, and they sometimes aid in traversing the DOM. For example:

var root = $('bodyContent'),

/* Does the root node have childNodes? */
if (root.hasChildNodes( )) {
    var temp = root.firstChild.nodeType;

    /* Find the /nodeType/ */
    switch (temp) {
        case 1:
            /* Does the /firstChild/ have an /id/ attribute? */
            if (root.firstChild.hasAttribute('id'))
                alert(root.firstChild.getAttribute('id'),
            break;
        case 3:
        case 4:
            alert(root.firstChild.data);
            break;
    }
}

I know this code doesn’t really do anything useful; it is here to show the use of several new methods and properties. The first new method in this code is hasChildNodes( ), which returns a Boolean value that is determined by the node having any child nodes. Next is the property nodeType, which returns a numeric value representing the type of the node. I introduced these numeric values to you in Table 5-1.

The first case statement in the code:

        case 1:
            /* Does the /firstChild/ have an /id/ attribute? */
            if (root.firstChild.hasAttribute('id'))
                alert(root.firstChild.getAttribute('id'),
            break;

introduces the hasAttribute( ) and getAttribute( ) methods. Just as you probably guessed, hasAttribute( ) returns a Boolean value based on whether the method finds an instance of the attribute being checked against. Likewise, getAttribute( ) returns the value of the attribute being asked for, and if no attribute exists, it returns an empty string. Given the following XHTML snippet, alerting $('myDiv').childNodes[2].getAttribute('id') would yield Figure 5-4:

<div id="myDiv">
    <p id="para_1">First paragraph</p>
    <p id="para_2">Second paragraph</p>
    <p id="para_3">Third paragraph</p>
    <p id="para_4">Fourth paragraph</p>
</div>
The value of the id attribute for the selected node alerted to the user

Figure 5-4. The value of the id attribute for the selected node alerted to the user

Finally, there is the data property, which contains the character data value of the node. The data property is valid only when checking on CDATASection, Comment, and Text node types.

Table 5-4 lists the methods available for gathering information about elements, attributes, and objects. In many cases, these methods get the values of the nodes they are part of, whereas in others they are testing values against conditions. Again, this table also lists which DOM interfaces are available to utilize the listed methods.

Table 5-4. Informational methods

Method

Description

Available interfaces

getAttribute(attrName)

Gets the value of the attribute with a name equal to the passed attrName.

Element

getAttributeNS(nsURI, localName)

Gets the value of the attribute with a namespace URI equal to the passed nsURI and a local name equal to the passed localName.

Element

hasAttribute(attrName)

Returns whether an attribute with a name equal to the passed attrName is specified on the element or has a default value.

Element

hasAttributeNS(nsURI, localName)

Returns whether an attribute with a namespace URI equal to the passed nsURI and a local name equal to the passed localName is specified on the element or has a default value.

Element

hasAttributes( )

Returns whether the node has any attributes.

All

hasChildNodes( )

Returns whether the node has any child nodes.

All

isSupported(feature, version)

Returns whether the passed feature with the passed version is supported on the node.

All

substringData(offset, count)

Returns a substring count * 16-bits in length from the data of the node starting at the passed offset. If the sum of offset and count exceeds the length, all data from the offset is returned.

CDATASection, Comment, Text

Table 5-5 lists the properties associated with nodes that you can use for informational purposes. You will recognize that most of these properties were used as either the returned value or the subject of a conditional test with the methods in Table 5-4.

Table 5-5. Informational properties

Property

Description

Available interfaces

data

The data set for the node.

CDATASection, Comment, Text

length

The number of nodes in the list, ranging from 0 to length −1

or

The number of characters (16-bit per character) available in the data attribute.

NodeList

or

CDATASection, Comment, Text

localName

The local part of the qualified name of the node.

All

name

The name of the attribute.

Attr

namespaceURI

The namespace URI of the node.

All

nodeName

The name of the node.

All

nodeType

Numeric code representing the type of the node. (See Table 5-1.)

All

nodeValue

The value of the node.

All

prefix

The namespace prefix of the node.

All

specified

A value of false if:

The Attr has a default value in the DTD, but no assigned value in the document.

A value of true if:

The Attr has an assigned value in the document.

The ownerElement is null (either it was just created or it was set to null).

Attr

tagName

The name of the element.

Element

value

The value of the attribute.

Attr

Walking the DOM

The methods and properties used to walk the DOM document tree are also the most-used and most-recognized of any of the methods and attributes we will see. This is simply a case of the most common tasks related to the DOM using these methods and properties to accomplish them (which is why they are so prevalent). We have already seen some of them in the examples in this chapter—methods such as getElementById( ) and getElementsByTagName( ).

The methods used to traverse the DOM are as simple as any of the other methods we have seen. For example:

var elements = getElementsByTagName('a'),
var array = new Array( );

/* Loop through the <a> elements */
for (i = 0, il = elements.length; i < il; i++)
    array[i] = elements.item(i).getAttributeNode('href').value;

We saw getElementsByTagName( ) already, so we will skip right to the getAttributeNode( ) method. This method returns the Attr node with a corresponding nodeName of the parameter that is passed. If there is no such node, the method returns null.

Table 5-6 lists the methods you can use to traverse a DOM document tree and which DOM interfaces can use them.

Table 5-6. Traversal methods

Method

Description

Available interfaces

getAttributeNode(nodeName)

Gets the Attr with a name equal to the passed nodeName.

Element

getAttributeNodeNS(nsURI, localName)

Gets the Attr with a namespace URI equal to the passed nsURI and a local name equal to the passed localName.

Element

getElementById(idName)

Gets the Element with an id equal to the passed idName.

Document

getElementsByTagName(tagName)

Gets a NodeList containing Elements with tagNames equal to the passed tagName.

Document, Element

getElementsByTagNameNS(nsURI, localName)

Gets a NodeList containing Elements with namespace URIs equal to the passed nsURI and local names equal to the passed localName.

Document, Element

item(index)

Returns the node in the list with an index equal to the passed index.

NodeList

Properties are also available to each node for stepping through a DOM document tree element by element. Consider this snippet from an XHTML page:

<div id="desserts">
    <ul id="cakes">
        <li id="cake1">Chocolate</li>
        <li id="cake2">Lemon</li>
        <li id="cake3">Cheesecake</li>
        <li id="cake4">Angelfood</li>
    </ul>
</div>

You could reference the third list element by using any of the following examples:

$('cakes').childNodes[2];
$('cake2').nextSibling;
$('cake4').previousSibling;
$('cakes').lastChild.previousSibling;
$('cake1').parentNode.childNodes[2];
$('cakes').firstChild.nextSibling.nextSibling;

These are just some of the many ways you can get to that third element. Table 5-7 lists the properties you can use to traverse the DOM document tree and the DOM interfaces to which each of them belongs.

Table 5-7. Traversal properties

Property

Description

Available interfaces

childNodes

A NodeList containing all of the children for this node, or an empty NodeList if there are no children.

All

documentElement

The root element of the document.

Document

firstChild

The first child of this node or null if there is no node.

All

lastChild

The last child of this node or null if there is no node.

All

nextSibling

The node immediately after this node or null if there is no node.

All

ownerDocument

The Document that the node is associated with or null if the node is a Document.

All

ownerElement

The Element node that the attribute is attached to or null if the Attr is not being used.

Attr

parentNode

The parent of this node. This attribute may be null if the node was just created or removed from a tree.

All

previousSibling

The node preceding this node or null if there is no node.

All

Change That Style

Just as methods and properties are available to developers to manipulate elements, attributes, and objects, so too are methods and properties available to manipulate the styles on a page programmatically. The methods and properties I describe here are part of the W3C’s Recommendation for the DOM. Note that Internet Explorer does not follow the W3C Recommendation for stylesheets in the DOM. I will cover this later in the chapter, in the section “What About Internet Explorer?”

When stylesheets are loaded into the DOM, whether it is by a <link> or a <style> element on the page, each rule that is imported has a rule type associated with it (see Table 5-8). The DOM can then access all of the imported rules and manipulate them according to the developer’s designs.

Table 5-8. CSS rule types

Rule type

Numeric type value

Unknown @ rule

0

Normal style rule

1

@charset rule

2

@import rule

3

@media rule

4

@font-face rule

5

@page rule

6

As you will see in the upcoming “Style Information” section, you can check these values before attempting code that may otherwise fail:

var rule = document.styleSheets[0].cssRules[0];
var URI = null;

/* Is the type equal to 3? */
if (rule.type == 3)
    URI = rule.href;

Modifying and Removing Style

Modifying stylesheets that are already in the DOM makes up a large part of what was coined DHTML (Dynamic HTML) back in 1998. You can use simple methods such as setProperty( ) and removeProperty( ) to do this, as in the following:

var styles = document.styleSheets[0].cssRules[0].style;


styles.setProperty('color', '#ff0000'),
styles.setProperty('font-size', '2em', 'important'),

styles.removeProperty('font-size'),
styles.removeProperty('color'),

The preceding code gets a particular style from the DOM’s stylesheet (in this example, it is arbitrary), and creates rules for the style using setProperty( ) while removing rules with removeProperty( ). The setProperty( ) method takes the name of the style, the value, and an optional priority for the style. To remove a style, whether it was loaded from a CSS file or was set programmatically, simply call the removeProperty( ) method and pass it the name of the style to remove. Table 5-9 lists all the W3C standard style methods.

Table 5-9. DOM stylesheet manipulation methods

Method

Description

appendMedium(mediaType)

Appends the passed mediaType to the list of media types associated with the stylesheet.

deleteMedium(mediaType)

Deletes the passed mediaType from the list of media types associated with the stylesheet.

cssRules[].deleteRule(index)

Deletes the CSS rule at the passed index within the media block, but only if the parent rule is an @media rule.

cssRules[].insertRule(rule, index)

Inserts the passed rule at the passed index within the media block, but only if the parent rule is an @media rule. If the passed index is equal to cssRules.length, the passed rule will be added at the end.

styleSheets[].deleteRule(index)

Deletes the CSS rule at the passed index.

styleSheets[].insertRule(rule, index)

Inserts the passed rule at the passed index within the stylesheet. If the passed index is equal to cssRules.length, the passed rule will be added at the end.

removeProperty(styleName)

Removes the style from the rule where the style equals the passed styleName.

setProperty(styleName, styleValue, priority)

Creates or replaces the style within the rule to the passed styleValue where the style is equal to the passed styleName. The priority is usually 'important' or an empty string.

Of course, using these methods is not the only way to manipulate the style on an element. The CSS2Properties object was made for just this purpose. For example:

$('subTitle').style.fontWeight = 'bold';

The CSS2Properties object is a convenient way to retrieve or set properties on an element. Setting an attribute using this method is just like calling the setProperty( ) method. The properties available (fontWeight, in this example) correspond to properties specified in the CSS 2.1 Recommendation. Table 5-10 lists all of these properties, along with the JavaScript-equivalent property and possible values.

Table 5-10. CSS2 properties and their JavaScript equivalents

CSS2.1 property name

JavaScript property name

Values

azimuth

azimuth

angle | left-side | far-left | left | center-left | center | center-right | right | far-right | right-side | behind | leftwards | rightwards

background

background

background-color | background-image | background-repeat | background-attachment | background-position

background-attachment

backgroundAttachment

scroll | fixed

background-color

backgroundColor

color | transparent

background-image

backgroundImage

URL | none

background-position

backgroundPosition

top left | top center | top right | center left | center center | center right | bottom left | bottom center | bottom right | x-percent y-percent | x-pos y-pos

background-repeat

backgroundRepeat

repeat | repeat-x | repeat-y | no-repeat

border

border

border-width | border-style | border-color

border-bottom

borderBottom

border-bottom-width | border-style | border-color

border-bottom-color

borderBottomColor

border-color

border-bottom-style

borderBottomStyle

border-style

border-bottom-width

borderBottomWidth

thin | medium | thick | length

border-collapse

borderCollapse

collapse | separate

border-color

borderColor

color

border-left

borderLeft

border-left-width | border-style | border-color

border-left-color

borderLeftColor

border-color

border-left-style

borderLeftStyle

border-style

border-left-width

borderLeftWidth

thin | medium | thick | length

border-right

borderRight

border-right-width | border-style | border-color

border-right-color

borderRightColor

border-color

border-right-style

borderRightStyle

border-style

border-right-width

borderRightWidth

thin | medium | thick | length

border-spacing

borderSpacing

length length

border-style

borderStyle

none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset

border-top

borderTop

border-top-width | border-style | border-color

border-top-color

borderTopColor

border-color

border-top-style

borderTopStyle

border-style

border-top-width

borderTopWidth

thin | medium | thick | length

border-width

borderWidth

thin | medium | thick | length

bottom

bottom

auto | percent | length

caption-side

captionSide

top | bottom | left | right

clear

clear

left | right | both | none

clip

clip

shape | auto

color

color

color-rgb | color-hex | color-name

content

content

string | URL | counter(name) | counter(name, list-style-type) | counters(name, string) | counters(name, string, list-style-type) | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote

counter-increment

counterIncrement

none | identifier number

counter-reset

counterReset

none | identifier number

cue

cue

cue-before | cue-after

cue-after

cueAfter

none | URL

cue-before

cueBefore

none | URL

cursor

cursor

URL | auto | crosshair | default | pointer | move | e-resize | ne-resize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize | text | wait | help

direction

direction

ltr | rtl

display

display

none | inline | block | list-item | run-in | compact | marker | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption

elevation

elevation

angle | below | level | above | higher | lower

empty-cells

emptyCells

show | hide

float

float

left | right | none

font

font

font-style | font-variant | font-weight | font-size/line-height | font-family | caption

font-family

fontFamily

family-name | generic-family

font-size

fontSize

xx-small | x-small | small | medium | large | x-large | xx-large | smaller | larger | length | percent

font-size-adjust

fontSizeAdjust

none | number

font-stretch

fontStretch

normal | wider | narrower | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded

font-style

fontStyle

normal | italic | oblique

font-variant

fontVariant

normal | small-caps

font-weight

fontWeight

normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900

height

height

auto | length | percent

left

left

auto | length | percent

letter-spacing

letterSpacing

normal | length

line-height

lineHeight

normal | number | length | percent

list-style

listStyle

list-style-type | list-style-position | list-style-image

list-style-image

listStyleImage

none | URL

list-style-position

listStylePosition

inside | outside

list-style-type

listStyleType

none | disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-alpha | upper-alpha | lower-greek | lower-latin | upper-latin | hebrew | armenian | georgian | cjk-ideographic | hiragana | katakana | hiragana-iroha | katakana-iroha

margin

margin

margin-top | margin-right | margin-bottom | margin-left

margin-bottom

marginBottom

auto | length | percent

margin-left

marginLeft

auto | length | percent

margin-right

marginRight

auto | length | percent

margin-top

marginTop

auto | length | percent

marker-offset

markerOffset

auto | length

marks

marks

none | crop | cross

max-height

maxHeight

none | length | percent

max-width

maxWidth

none | length | percent

min-height

minHeight

length | percent

min-width

minWidth

length | percent

orphans

orphans

number

outline

outline

outline-color | outline-style | outline-width

outline-color

outlineColor

color | invert

outline-style

outlineStyle

none | dotted | dashed | solid | double | groove | ridge | inset | outset

outline-width

outlineWidth

thin | medium | thick | length

overflow

overflow

visible | hidden | scroll | auto

padding

padding

padding-top | padding-right | padding-bottom | padding-left

padding-bottom

paddingBottom

length | percent

padding-left

paddingLeft

length | percent

padding-right

paddingRight

length | percent

padding-top

paddingTop

length | percent

page

page

auto | identifier

page-break-after

pageBreakAfter

auto | always | avoid | left | right

page-break-before

pageBreakBefore

auto | always | avoid | left | right

page-break-inside

pageBreakInside

auto | avoid

pause

pause

pause-before | pause-after

pause-after

pauseAfter

time | percent

pause-before

pauseBefore

time | percent

pitch

pitch

frequency | x-low | low | medium | high | x-high

pitch-range

pitchRange

number

play-during

playDuring

auto | none | URL | mix | repeat

position

position

static | relative | absolute | fixed

quotes

quotes

none | identifier number

richness

richness

number

right

right

auto | length | percent

size

size

auto | portrait | landscape

speak

speak

normal | none | spell-out

speak-header

speakHeader

always | once

speak-numeral

speakNumeral

digits | continuous

speak-punctuation

speakPunctuation

none | code

speech-rate

speechRate

number | x-slow | slow | medium | fast | x-fast | faster | slower

stress

stress

number

table-layout

tableLayout

auto | fixed

text-align

textAlign

left | right | center | justify

text-decoration

textDecoration

none | underline | overline | line-through | blink

text-indent

textIndent

length | percent

text-shadow

textShadow

none | color | length

text-transform

textTransform

none | capitalize | uppercase | lowercase

top

top

auto | length | percent

unicode-bidi

unicodeBidi

normal | embed | bidi-override

vertical-align

verticalAlign

baseline | sub | super | top | text-top | middle | bottom | text-bottom | length | percent

visibility

visibility

visible | hidden | collapse

voice-family

voiceFamily

specific-voice | generic-voice

volume

volume

number | percent | silent | x-soft | soft | medium | loud | x-loud

white-space

whiteSpace

normal | pre | nowrap

widows

widows

number

width

width

auto | length | percent

word-spacing

wordSpacing

normal | length

z-index

zIndex

auto | number

Suppose we have the following code:

var styles = document.styleSheets[0].cssRules[0].style;

styles.setProperty('border', '2px solid #000000'),
styles.setProperty('background-color', '#ff0000'),
styles.setProperty('font-size', '2em'),
styles.setProperty('z-index', 10);
styles = document.styleSheets[0].cssRules[1].style;
styles.setProperty('background-color', '#0000ff'),
styles.setProperty('font-style', 'italic'),

This gives us something like Figure 5-5.

A page manipulated with CSS rules

Figure 5-5. A page manipulated with CSS rules

Implementing the following code will change the page to something like Figure 5-6:

var styles = document.styleSheets[0].cssRules[0].style;

styles.removeProperty('z-index'),
styles.addProperty('top', '5px'),
styles.addProperty('border-style', 'dashed'),
styles = document.styleSheets[0].cssRules[1].style;
styles.removeProperty('font-style'),
styles.addProperty('background-color', '#00ff00'),
The page changed programmatically

Figure 5-6. The page changed programmatically

When you’re using the CSS shorthand properties, you should break down the shorthand into the component longhand when appropriate. When getting the values, the shortest form equivalent to the declarations made in the ruleset should be returned. If no shorthand can be added, it should contain an empty string.

For example, this should not be returned:

bold normal normal 12pt "Courier New", monospace

when this will do:

bold 12pt "Courier New", monospace

The normals are default values, and they are implied in the longhand properties should they be queried.

Style Information

Only a few methods are available for getting to the information in a stylesheet or rule. These methods function in basically the same way. Take the following, for example:

var styles = document.styleSheets[0].cssRules[0].style;

/* Does the style sheet have a color property priority? */
if (styles.getPropertyPriority('color'))
    alert(styles.cssText);
else
    styles.setProperty('color', styles.getPropertyValue('color'), 'important'),

This example checks whether the color style name has been given a priority using the getPropertyPriority( ) method. If it has, it alerts the cssText of the style; otherwise, it sets the property to have a priority of 'important', using its existing value (retrieved using the getPropertyValue( ) method) in the setProperty( ) method. Table 5-11 describes all the methods used to gather information using the CSS DOM.

Table 5-11. Informational DOM stylesheet methods

Method

Description

getPropertyPriority(styleName)

Gets the priority of the style with a name equal to the passed styleName.

getPropertyValue(styleName)

Gets the value of the style with a name equal to the passed styleName.

media.item(index)

Returns the name of the media type at the index equal to the passed index.

style.item(index}

Returns the style at the index equal to the passed index, within the associated rule.

Along with the methods listed in Table 5-11 are properties you can use in both a read and a write manner (see Table 5-12). Reading these properties gives you the information on a stylesheet or rule, while utilizing the property to modify a stylesheet or rule can offer the benefit of direct access that methods do not give.

Table 5-12. Informational DOM stylesheet properties

Property

Description

cssRules[].cssText

The text that represents the given rule, including the selector and styles.

style.cssText

The text that represents the style part of the rule.

disabled

The Boolean value indicating whether the associated stylesheet is disabled.

encoding

The encoding for the rule, if the rule is an @charset rule.

cssRules[].href

The URL for the rule, if the rule is an @import rule.

styleSheets.href

The URL of the stylesheet.

media.length

The browser’s interpretation of the number of media types to which the associated stylesheet applies.

style.length

The browser’s interpretation of the number of styles inside the associated rule.

mediaText

The textual representation of the media types to which the stylesheet applies.

nameOfStyle

The textual representation of the named style value.

cssRules[].selectorText

The textual representation of the selector part of the rule, but only if it is a normal rule or an @page rule.

rules[].selectorText

The textual representation of the selector part of the rule.

title

The title attribute of the style or link element that creates the associated stylesheet.

cssRules[].type

The numerical representation of the rule type (see Table 5-8, earlier in this chapter).

styleSheets[].type

The type attribute of the style or link element that creates the associated stylesheet.

An example of using a property for writing follows:

document.styleSheets[0].cssRules[5].style.cssText = 'color: #ff0000; ' +
    'font-size: 2em !important;';

The preceding line of code takes the place of these lines:

var styles = document.styleSheets[0].cssRules[5].style;

styles.setProperty('color', '#ff0000'),
styles.setProperty('font-size', '2em', 'important'),

As I said at the beginning of this section, these methods and properties are part of the W3C Recommendation. So, how do things differ with Internet Explorer?

What About Internet Explorer?

Internet Explorer 6.0 and earlier do not support many of the DOM 2 stylesheet methods or properties. Their alternatives are not as complete, but they do handle basic manipulation of stylesheet rules. The stylesheet collection itself is the same as all the other standards-compliant browsers, and it works in basically the same way.

The first difference is in referencing the stylesheet’s creator. For standards-compliant browsers the property is ownerNode, but in Internet Explorer the property is owningElement, as in this example:

var sheet = document.styleSheets[0];
var element = ((sheet.ownerNode) ? sheet.ownerNode : sheet.owningElement);

Internet Explorer does have the same collection as with standards-compliant browsers—the disabled, href, title, and type properties all work in the same manner—but the media property is different. With standards-compliant browsers the property is an object, but Internet Explorer treats it as a string. For this reason, if you wish to alter it, you must alter the string. Internet Explorer has no methods to add, remove, or list media types because it is not an object:

var sheet = document.styleSheets[0];

/* Does the media type have a type of /string/? */
if (typeof sheet.media == 'string')
    sheet.media = 'screen';
else
    sheet.media.mediaText = 'screen';

The preceding code checks to see what browser is being used so that it knows what property to set. If, however, you are coding for Internet Explorer for the Mac, trying to set the media property to a string will throw an error. Therefore, the code will need to have an additional check to work properly:

var sheet = document.styleSheets[0];

/* Does the media type have a type of /string/? */
if (typeof sheet.media == 'string')
    try { sheet.media = 'screen' } catch(ex) {};
else
    sheet.media.mediaText = 'screen';

You must do this for Internet Explorer for the Mac because the media property is read-only in this browser.

The styleSheet property in Internet Explorer for Windows works in the same way as the sheet property for standards-compliant browsers. This property, however, is not available in Internet Explorer for the Mac.

As standards-compliant browsers have the cssRules collection, so too does Internet Explorer provide the rules collection. The methods and properties available to Internet Explorer are not compatible with those of the standards-compliant browsers. It is not possible to index the same rule in each collection, as @charset, @import, @media, @font-face, and @page rules are not included in the rules collection. @media blocks are included in the rules collection of the stylesheet in Internet Explorer for Windows, but in Internet Explorer for the Mac they are ignored, as they are not available to the DOM. For Internet Explorer in Windows, you cannot add new rules into @media blocks.

Tip

A cssText property in Internet Explorer is available directly in the stylesheet. This includes any @media blocks in a Windows environment; however, this property can create editing difficulties because some sort of pattern-matching is required.

Internet Explorer for the Mac has both the rules and the cssRules collections available in the DOM, but they are both treated the Internet Explorer way. Because of this, you should check the rules collection first, and if it’s available, you should use it before you consider the cssRules collection:

var sheet = document.styleSheet[0];
var rule = ((sheet.rules) ? Sheet.rules[4] : sheet.cssRules[5]);

Internet Explorer provides a removeRule( ) method that functions exactly as the deleteRule( ) method does, and it provides an addRule( ) method. But this method does not function like the insertRule( ) method does:

/* Is there an insertRule( ) method available? */
if (sheet.insertRule)
    sheet.insertRule('div#special { font-size: 1.5em; color: #f00; }',
                     sheet.cssRules.length);
/* Is there an addRule( ) method available? */
else if (sheet.addRule)
    sheet.addRule('div#special', 'font-size: 1.5em; color: #f00;'),

This section just scratched the surface regarding the differences between Internet Explorer and standards-compliant browsers. However, it is beyond the scope of this book to discuss all of the differences. You can find more information on how Internet Explorer handles stylesheets on MSDN, at http://msdn.microsoft.com/workshop/author/css/css_node_entry.asp.

Events in the DOM

The ability to manipulate events on the client is central to Web 2.0 and Ajax web applications. Whether it is a user moving the mouse over an object on the application, or typing some text, or clicking on a button, the events that fire from these actions are paramount to having any client-application interaction. All client events are broken out by Event modules. These modules are as follows:

HTMLEvent module

abort, blur, change, error, focus, load, reset, resize, scroll, select, submit, unload

UIEvent module

DOMActivate, DOMFocusIn, DOMFocusOut, keydown, keypress, keyup

MouseEvent module

click, mousedown, mousemove, mouseout, mouseover, mouseup

MutationEvent module

DOMAttrModified, DOMNodeInserted, DOMNodeRemoved, DOMCharacterDataModified, DOMNodeInsertedIntoDocument, DOMNodeRemovedFromDocument, DOMSubtreeModified

Nonstandard Event module

Nonstandard events that do not really fit in the other modules

Before you can use any of these events, you must create and initialize them. The DOM enables developers to fully manipulate an event, no matter what it is. We will look at this next.

Creating Events

You can create most events by simply attaching the function or JavaScript action you want to fire directly to the event. Consider these examples:

<a href="/favorites/" onclick="close_all( );">My Favorites</a>

<input id="username" name="nptUsername" type="text" value=""
    onblur="check_user(this);" />

<body onload="initialize( );">

If, however, you need to synthesize an event from within the application code itself, the DOM provides the createEvent( ) method. For example:

var evt = document.createEvent('MouseEvents'),

If the browser supports an eventType parameter that is passed to the method, the method will return a new Event of the type passed. After the event is created, you must call the specific Event initiation method to complete the creation. When the browser does not recognize the eventType passed, you can still dispatch it within the client if you implement your own Event initialization method.

Initializing, Firing, Adding, and Removing Events

Once a new event has been created, it is ready to be initialized and dispatched to the client application. Four methods are available for initializing an Event, each for a specific eventType, as shown in Table 5-13.

Table 5-13. Event initialization methods

Method

Description

InitEvent(eventType, bubbles, cancelable)

Initializes the event as a generic event, without defining additional properties.

InitMouseEvent(eventType, bubbles, cancelable, window, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)

Initializes a MouseEvent event as a mouse event.

InitMutationEvent(eventType, bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange)

Initializes a MutationEvent event as a mutation event.

InitUIEvent(eventType, bubbles, cancelable, window, detail)

Initializes the event as a generic UI event, without defining additional properties, and is available for MouseEvent and UIEvent events.

This example shows the creation and initialization of a MouseEvent event:

var evt = document.createEvent('MouseEvents'),

evt.initMouseEvent('click', true, true, window, 20, 200, 26, 208, false,
    false, true, false, 0, null);
$('nptSpecial').dispatchEvent(evt);

You will notice that after the initMouseEvent( ) method, a call to the dispatchEvent( ) method is required to actually set the new Event within the client. The dispatchEvent( ) method takes the form dispatchEvent(eventObject).

The title of this section may be a bit misleading. The adding and removing actually do not pertain to the event itself; they pertain to event listeners. Adding an event listener to an element is fairly simple. You use the addEventListener( ) method to add a listener to a particular event type. For example:

var myElement = $('myDiv'),

myElement.addEventListener('click', function(e) { // do something }, true);

The addEventListener( ) method takes for parameters the event to listen to, the function to fire when the event occurs, and a phase which can be true for capture and false for bubble.

Similarly, to remove an event listener for an object, a developer would use the removeEventListener( ) method. This method takes for parameters the event to stop listening to and a phase that can be true for capture and false for bubble, as follows:

myElement.removeEventListener('click', arguments.callee, false);

Event Information

All Event objects contain a number of methods and properties that you can use to obtain information about the event. For example:

var link = $('firstLink'),

link.addEventListener('click', function(e) {
    /* Is the event cancelable? */
    if (e.cancelable)
        e.preventDefault( );
    launchWindow( );
}, false);

This event listener checks whether the event can be canceled, and if it can, it calls the method preventDefault( ), which prevents the cancellation of the event. Then a function that launches a window is called. Table 5-14 lists the methods contained in the Event, EventCapturer, and EventListener objects, along with descriptions and the object to which each belongs.

Table 5-14. Event methods

Method

Description

Object

captureEvent(eventType)

Captures the particular type of event that is passed in eventType.

EventCapturer

handleEvent(event)

Handles the event whenever it occurs for the EventListener to which it was registered.

EventListener

preventDefault( )

Prevents any default action from firing as long as the Event is cancelable.

Event

releaseEvent(eventType)

Stops capturing the particular type of event that is passed in eventType.

EventCapturer

routeEvent( )

Continues the event’s flow to additional event handlers, and if none is present, to the target of the event.

EventCapturer

stopPropagation( )

Stops any further propagation of an event during any phase of the event.

Event

Table 5-15 lists the properties contained in an Event object, along with a description and the eventType to which each belongs.

Table 5-15. Event properties

Property

Description

Event type

altKey

The Boolean indicator as to whether the Alt key was pressed when the event was fired.

MouseEvent

attrChange

The indicator of what type of change was triggered with a DOMAttrModified event. Values are:

  • 1 = Modification

  • 2 = Addition

  • 3 = Removal

MutationEvent

attrName

The string of the changed Attr node in a DOMAttrModified event.

MutationEvent

bubbles

The Boolean indicator as to whether the event is a bubbling event.

All

button

The button that was pressed or released when the mouse button changed state. The values for the button can be:

  • 0 = Left mouse button

  • 1 = Middle mouse button

  • 2 = Right mouse button

For left-handed mice, the values are reversed.

MouseEvent

cancelable

The Boolean indicator as to whether the event can have its default action prevented.

All

clientX

The horizontal coordinate at which the event happened, relative to the client area.

MouseEvent

clientY

The vertical coordinate at which the event happened, relative to the client area.

MouseEvent

ctrlKey

The Boolean indicator as to whether the Ctrl key was pressed when the event was fired.

MouseEvent

currentTarget

The reference to the element currently processing the event.

All

detail

The detail information about the Event, depending on its type.

UIEvent, MouseEvent

eventPhase

The phase of the event currently being processed. Phases are:

  • 0 = A manually created event object that has yet to be fired

  • 1 = Capture phase

  • 2 = Bubble phase on the target element

  • 3 = During the bubbling phase on the target’s ancestors

All

metaKey

The Boolean indicator as to whether the Meta key was pressed when the event was fired. This is the Windows key for Windows and the Apple/Command key for Macs.

MouseEvent

newValue

The new value of the node after a mutation event.

MutationEvent

prevValue

The previous value of the node before a mutation event.

MutationEvent

relatedNode

The secondary node related to the mutation event.

MutationEvent

relatedTarget

The secondary event target related to the mouse event.

MouseEvent

screenX

The horizontal coordinate at which the event happened, relative to the origin of the screen coordinate system.

MouseEvent

screenY

The vertical coordinate at which the event happened, relative to the origin of the screen coordinate system.

MouseEvent

shiftKey

The Boolean indicator as to whether the Shift key was pressed when the event was fired.

MouseEvent

target

The target to which the event was originally dispatched.

All

timeStamp

The time in milliseconds at which the Event was created.

All

type

The XML name of the event.

All

view

The view from which the event was generated.

UIEvent, MouseEvent

What About Internet Explorer? Part II

Internet Explorer simply does not provide any of the DOM 2 Events methods, and there are only a couple of the same properties of the Event object. Versions starting at 5 provide an event system that is similar in nature, but is more limited in functionality.

You will recall that for standards-compliant browsers, you use the methods createEvent( ), init*Event( ), and dispatchEvent( ) to successfully create, initialize, and dispatch an event to an element, respectively. In Internet Explorer, similar methods are available, but initializing an Event object is a little cruder, as shown in Example 5-2.

Example 5-2. Initializing an Event object for Internet Explorer

var special = $('nptSpecial'),

/* Does the document have a /createEvent( )/ method? */
if (document.createEvent) {
    var evt = document.createEvent('MouseEvents'),

    evt.initMouseEvent('click', true, true, window, 0, 20, 200, 26, 208,
        false, false, true, false, 0, null);
    special.dispatchEvent(evt);
/* Does the document have a /createEventObject( )/ method? */
} else if (document.createEventObject) {
    var evt = document.createEventObject( );

    evt.detail = 0;
    evt.screenX = 20;
    evt.screenY = 200;
    evt.clientX = 26;
    evt.clientY = 208;
    evt.ctrlKey = false;
    evt.altKey = false;
    evt.shiftKey = true;
    evt.metaKey = false;
    evt.button = 0;
    evt.relatedTarget = null;
    special.fireEvent('onclick', evt);'
}

The createEventObject( ) method creates an empty Event object, unless an existing Event object is passed to it. In this case, the passed object is used as a template when creating the new object. Instead of calling an init*Event( ) method, you must set each property of the Event object individually. Finally, instead of calling the dispatchEvent( ) method, you call the Internet Explorer fireEvent( ) method. This method takes the event type and the event object itself.

You cannot find in Internet Explorer the addEventListener( ) and removeEventListener( ) methods that are used in standards-compliant browsers. Instead, you use the attachEvent( ) and removeEvent( ) methods. They function in almost the same way, as shown here:

function handleMyEvent(e) {
    // do something here
}

var special = $('nptSpecial'),

/* Is there an /addEventListener( )/ method? */
if (special.addEventListener)
    special.addEventListener('click', handleMyEvent, false);
/* Is there an /attachEvent( )/ method? */
else if (special.attachEvent)
    special.attachEvent('onclick', handleEvent);

Internet Explorer does not support canceling of an event; it supports only bubbling. Therefore, you cannot call the stopPropagation( ) method. Instead, the cancelBubble property is provided:

/* Is there a /stopPropagation( )/ method? */
if (e.stopPropagation)
    e.stopPropagation( );
else
    e.cancelBubble = true;

Internet Explorer also does not support stopping default actions, so the preventDefault( ) method will not work. Internet Explorer instead provides the returnValue property:

/* Is there a /preventDefault( )/ method? */
if (e.preventDefault)
    e.preventDefault( );
else
    e.returnValue = false;

DOM Stuff for Tables

XHTML tables have methods and properties that the other XHTML elements do not have. These special methods and properties are specifically designed for manipulating parts of the table in a more precise manner. To use these methods and properties, however, you must think of a table in the full XHTML specification. An XHTML table contains a <caption>, a <thead>, a <tfoot>, and any number of tbodies:

caption

References the <caption> of a table

thead

References the <thead> of a table, if there is one

tfoot

References the <tfoot> of a table, if there is one

tbodies

Reference a collection with one entry for every <tbody> that exists for the table (there is usually just one <tbody>, table.tbodies[0])

A rows collection corresponds to all the rows in each <thead>, <tfoot>, and <tbody> node. Each row has a cells collection, which contains every <td> or <th> element in that given row. Every cell contains all of the normal DOM methods and properties associated with an XHTML element. Consider the following table, which is displayed in Figure 5-7:

<table id="oreillyBooks" summary="Some O'Reilly books on Ajax">
    <caption>O'Reilly Ajax Books</caption>
    <thead>
        <tr>
            <th>Title</th>
            <th>Author(s)</th>
            <th>Published Date</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <td colspan="3">Ajax books from oreilly.com</td>
        </tr>
    </tfoot>
    <tbody>
        <tr>
            <td>Ajax Design Patterns</td>
            <td>Michael Mahemoff</td>
            <td>June 2006</td>
        </tr>
        <tr>
            <td>Ajax Hacks</td>
            <td>Bruce W. Perry</td>
            <td>March 2006</td>
        </tr>
        <tr>
            <td>Head Rush Ajax</td>
            <td>Brett McLaughlin</td>
            <td>March 2006</td>
        </tr>
        <tr>
            <td>Programming Atlas: Rough Cuts</td>
            <td>Christian Wenz</td>
            <td>March 2006</td>
        </tr>
    </tbody>
</table>
An XHTML table to be manipulated by the DOM

Figure 5-7. An XHTML table to be manipulated by the DOM

Using the DOM properties to reference elements in the table, here are some examples of how to reference table nodes:

var table = $('oreillyBooks'),

x = table.tBodies[0].rows[0].cells[1].firstChild.value; // Michael Mahemoff
x = table.tHead.rows[0].cells[2].firstChild.value; // Published Date
x = table.tBodies[0].rows[2].cells[0].firstChild.value; // Ajax Design Patterns
x = table.tFoot.rows[0].cells[0].firstChild.value; // Ajax books from oreilly.com

Tables, along with their child elements, have methods that you can use for creating, inserting, and deleting, as shown in Table 5-16.

Table 5-16. DOM table methods

Method

Description

Element

createCaption( )

Creates a new table caption object, or returns an existing one.

HTMLTableElement

createTFoot( )

Creates a table footer row, or returns an existing one.

HTMLTableElement

createTHead( )

Creates a table header row, or returns an existing one.

HTMLTableElement

deleteCaption( )

Deletes the table caption, if one exists.

HTMLTableElement

deleteCell(index)

Deletes a cell from the current row at the passed index. If the index is -1, the last cell in the row is deleted.

HTMLTableRowElement

deleteRow(index)

Deletes a table row found at the passed index. If the index is −1, the last row in the table is deleted.

HTMLTableElement, HTMLTableSectionElement

deleteTFoot( )

Deletes the footer from the table, if one exists.

HTMLTableElement

deleteTHead( )

Deletes the header from the table, if one exists.

HTMLTableElement

insertCell(index)

Inserts an empty cell into this row at the passed index. If the index is −1 or is equal to the number of cells, the new cell is appended.

HTMLTableRowElement

insertRow(index)

Inserts a table row found at the passed index. If the index is -1 or is equal to the number of rows, the new row is appended to the last row.

HTMLTableElement, HTMLTableSectionElement

The methods are easy to use, as the descriptions in the table of methods show. The following is an example of the createCaption( ) method:

var x = $('myTable').createCaption( );

x.appendChild(document.createTextNode('This is my table caption'));

Likewise, it’s easy to use methods such as insertRow( ) and insertCell( ), as the following illustrates:

var x = $('myTable').insertRow(2);
var a = x.insertCell(0);
var b = x.insertCell(1);
var c = x.insertCell(2);

a.appendChild(document.createTextNode('New data in column one'));
b.appendChild(document.createTextNode('New data in column two'));
c.appendChild(document.createTextNode('New data in column three'));

Using the table of O’Reilly books that produced Figure 5-7, this code would produce Figure 5-8. That’s all there really is to manipulating tables using DOM methods and properties. Of course, most of the normal DOM properties and methods could accomplish the same things, but the DOM table methods and properties make things simpler.

The DOM manipulated table

Figure 5-8. The DOM manipulated table

Is innerHTML Evil?

The innerHTML property has caused much debate since Microsoft introduced it for Internet Explorer all those years ago. There are usually only two camps on this issue: those that support it wholeheartedly and those that believe it is evil. So, the question that needs to be answered is “Is innerHTML evil?”

First, a little bit about innerHTML. innerHTML allows a developer to create a string of XHTML and set the innerHTML property equal to that string. The browser is then tasked with translating all of the XHTML elements to create a DOM document tree out of the string. For example:

var string = '<div id="myDiv"><p>Paragraph One</p><p>Paragraph <b>Two</b></p></div>';

$('contentBody').innerHTML = string;

Now, consider the DOM methods required to create the same string using only W3C standards. Here is an example:

/* Create some new elements? */
var outerDiv = document.createElement('div'),
var para1 = document.createElement('p'),
var para2 = document.createElement('p'),
var bold = document.createElement('b'),

/* Create the attributes and nodes */
outerDiv.setAttribute('id', 'myDiv'),
para1.appendChild(document.createTextNode('Paragraph One'));
para2.appendChild(document.createTextNode('Paragraph '));
bold.appendChild(document.createTextNode('Two'));
para2.appendChild(bold);
outerDiv.appendChild(para1);
outerDiv.appendChild(para2);

/* Append the new <div> element to the /contentBody/ element */
$('contentBody').appendChild(outerDiv);

Look at how many more lines it took to build this same bit of code as XML! So, why doesn’t everyone just switch to innerHTML and forget about all of those DOM methods? Let’s examine some of the pros and cons of innerHTML.

innerHTML does not require nearly as many lines to create a large string of XHTML as the W3C standards do. This can be an advantage when a developer is trying to keep the size of her JavaScript files as small as possible. Also, innerHTML is well supported by all of the major browser makers. It is kind of amusing that innerHTML is, in fact, better supported than some of the W3C standard methods and properties.

When it comes to creating Ajax pages, innerHTML can come in very handy. It is extremely easy to take the responseText from an XMLHttpRequest object and set some element’s innerHTML to this response. Isn’t the whole point of Ajax to refresh the content of part of the page as quickly as possible? This is yet another advantage of using innerHTML—it is faster than building the content using the DOM methods and properties.

On the other hand, innerHTML is a proprietary property. It may be widely supported now, but that does not mean it will be in the future. Unless innerHTML becomes a W3C standard, there is no way anyone can know whether future browsers will support it. With that aside, using a proprietary property is not so bad. The XMLHttpRequest object that I introduced in Chapter 4 is also proprietary.

Another problem with innerHTML is that whether the string passed to the innerHTML property contains valid and well-formed markup or not, it is still shoved into the property. It may be more time-consuming, but it is safer to use methods such as createElement( ), createTextNode( ), and appendChild( ).

Plus, MSDN’s definition of innerHTML is that the property is read-only for the following elements: <col>, <colgroup>, <frameset>, <html>, <style>, <table>, <tbody>, <tfoot>, <thead>, <title>, and <tr>. The problem becomes readily apparent. The innerHTML property does not work when you’re trying to add content from within any of these elements. This makes creating dynamic content within tables an impossible task using innerHTML. Furthermore, innerHTML continues to have problems that are documented in the editorial “innerHTML Gotchas,” which you can find at http://www.ajaxian.com/archives/innerhtml-gotchas.

It will always be up to the developer whether to use innerHTML or W3C standard methods and properties. For my money, I say why not both? Sometimes it makes sense to use innerHTML—when speed is a factor, for example. Other times—such as when data needs to be dynamically appended to a table—using DOM methods and properties is better. So, to answer the question of whether innerHTML is evil: sometimes it is and sometimes it is not.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset