DOM Utilities

Recall that Dojo intentionally does not attempt to replace core JavaScript functionality; on the contrary, it only augments it where value can be added so that you can write portable code and incur less boilerplate. For this reason, you won't see direct replacements for common DOM operations such as appendChild, removeChild, and so on. Still, there are many utilities that could make DOM manipulation a lot simpler, and this section is all about how Base helps to make that happen.

Ancestry

Base packs several useful functions that augment and supplement common DOM functions. The first of these functions, isDescendant, shown in Table 2-1, is self-descriptive. You provide it two arguments (id values or actual nodes), where the first argument is the node of interest and the second argument is a potential ancestor. If the node of interest is in fact a member of the potential ancestor's DOM tree, the function returns true.

Table 2-1. Base function for manipulating and handling the DOM

Name

Return type

Comment

dojo.isDescendant(/*String | DomNode*/node, /* String | DomNode*/potentialAncestor)

Boolean

Returns a Boolean value indicating if a node has a particular ancestor or not and works in nested hierarchies as would be expected.

Selectability

The need to make a text on the page unselectable via the cursor is not uncommon and sometimes can actually enhance usability. Virtually every browser has a specific way of accomplishing this task, but no need to worry—you have Dojo. Whenever the need arises, just use the dojo.setSelectable function. Here's the self-descriptive API:

dojo.setSelectable(/*String | DomNode*/node, /*Boolean*/selectable)

Tip

Hopefully, it goes without saying that no client-side operation should ever be relied on to protect sensitive content because if something is being viewed in the browser as a native display, it can and will be reverse-engineered.

Styling Nodes

Base's dojo.style function provides a comprehensive means of getting or setting individual style values for a particular node. Simply provide the node and a style value in DOM-accessor format (e.g., borderWidth, not border-width ) to fetch a particular style value. Providing style value in DOM-accessor format as a third argument causes the function to act as a setter method instead of a getter method. For example, dojo.style("foo", "height") would return the height of element with an id of "foo", while dojo.style("foo", "height", "100px") would set its height to 100 pixels. You can also set multiple style properties at the same time by using an Object as the second parameter, like so:

dojo.style("foo", {
    height : "100px",
    width : "100px",
    border : "1px green"
});

While many applications benefit from dojo.style 's ability to manipulate specific style attributes, there is just as common a need for adding, removing, toggling, and checking for the existence of a particular class. Base's suite of functions for manipulating class can do just that, and they all share a common function signature. The first parameter is the DOM node of interest, and the second parameter is a string value indicating the class to manipulate. For example, adding a class to a node is as simple as dojo.addClass("foo", "someClassName"). Note that the class name does not include a leading dot as would define it in the stylesheet.

Table 2-2 summarizes the various facilities for manipulating the appearance of a node.

Table 2-2. Base functions for style handling

Name

Comment

dojo.style(/*DomNode|String*/ node, /*String?|Object?*/style, /*String?*/value)

Provides a means of getting and setting specific style values on a node.

dojo.hasClass(/*DomNode*/node, /*String*/classString)

Returns true only if node has a particular class applied to it.

dojo.addClass(/*DomNode*/node, /*String*/classString)

Adds a particular class to a node.

dojo.removeClass(/*DomNode*/node, /*String*/classString)

Removes a particular class from a node.

dojo.toggleClass(/*DomNode*/node,/*String*/classString)

Adds a class if a node does not have it; removes a class if it does have it.

Manipulating Attributes

Mimicking the same approach as the previous section discussed for styling nodes, Base also provides functions for normalizing the ability to set, get, check for the existence of, and remove attributes. Table 2-3 lists the available functions.

Table 2-3. Base functions for manipulating node attributes

Name

Comment

dojo.attr(/*DOMNode|String*/node, /*String?|Object?*/attrs, /*String?*/value)

Provides a means of getting and setting attributes for a node.

dojo.hasAttr (/*DOMNode|String*/node, /*String*/name)

Returns true only if node has a particular attribute.

dojo.removeAttr (/*DOMNode|String*/node, /*String*/name)

Removes an attribute from a node.

The dojo.attr function works just like dojo.style in that it can set values for individual attributes or multiple attributes depending on whether you use the second and third parameters to specify an attribute and its value, or if you provide an associative array as the second parameter that contains a collection of attributes and values. The hasAttr and removeAttr functions are self-descriptive and work just as you would expect.

Placing Nodes

The built-in methods for manipulating DOM content such as appendChild, insertBefore, and so on can get the job done, but sometimes it's a lot more convenient to have a uniform means of placing nodes, and the dojo.place function, documented in Table 2-4, provides just that. In a nutshell, you give it three parameters: a node to be placed, a reference node, and a position that defines the relative relationship. The position parameter may take on the values "before", "after", "first", and "last". The values "before" and "after" may be used to for relative placement in a lateral context, while "first" and "last" may be used for absolute placement in a context that assumes the reference node is the parent of the node being placed. Position may also be supplied as an Integer value, which refers to the absolute position that the node to be placed should have in the reference node's child nodes.

Table 2-4. Placing a node

Name

Comment

dojo.place(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position)

Augments DOM functionality by providing a uniform function for inserting a node relative to another node. Returns a Boolean.

The Box Model

The CSS box model is a fairly simple topic, but because there are so many inconsistent implementations of it that are available on the Web, things get messy pretty quickly. This short section does little more than scratch the surface, because you really do want to turn to an authoritative reference such as Eric Meyer's CSS: The Definitive Guide (O'Reilly) to really get to the bottom of it all.

Tip

If various inconsistent implementations of the box model aren't enough, there's also the issue of keeping the CSS2 box model and the CSS3 box model straight. You can read about the CSS2 box model in the CSS2 Specification at http://www.w3.org/TR/REC-CSS2/box.html, while the CSS3 working draft is at http://www.w3.org/TR/css3-box/.

The ultra-condensed version of the story, however, is that the box model was designed as a way of providing flexible visual formatting that controls the height and width of content by arranging a series of nested boxes around a page element. Before any more dialogue, take a look at Figure 2-2, which conveys the basic idea.

The behavior of width and height as defined by CSS 2.1 Box Model

Figure 2-2. The behavior of width and height as defined by CSS 2.1 Box Model

To summarize the differences between content, margin, padding, and border boxes, review the following relevant blurb from the specification:

The margin, border, and padding can be broken down into left, right, top, and bottom segments (e.g., in the diagram, "LM" for left margin, "RP" for right padding, "TB" for top border, etc.). The perimeter of each of the four areas (content, padding, border, and margin) is called an "edge," so each box has four edges:

1 - content edge or inner edge

The content edge surrounds the element's rendered content.

2 - padding edge

The padding edge surrounds the box padding. If the padding has 0 width, the padding edge is the same as the content edge. The padding edge of a box defines the edges of the containing block established by the box.

3 - border edge

The border edge surrounds the box's border. If the border has 0 width, the border edge is the same as the padding edge.

4 - margin edge or outer edge

The margin edge surrounds the box margin. If the margin has 0 width, the margin edge is the same as the border edge.

As it turns out, two different means of realizing the box model emerged, which is where the divergence begins: the content-box and the border-box. The basic difference between the two approaches can be captured by asking what defines how margins and borders are applied to the content area. With the content-box approach, any area incurred by padding and borders is accounted for outside of the explicit width and height of the content, whereas the border-box approach calls for any padding and borders to be accounted for inside the explicit height and width of the content area. In other words, the content-box approach associates a height/width strictly with only the content, whereas the border-box approach associates a height/width with the border inward.

Tip

Many modern browsers support two modes: standards mode and quirks mode. The content-box approach is associated with standards mode while the border-box approach is associated with quirks mode.

If you're not doing anything very fancy and just want to space out some content, the differences may not be apparent, and you can generally get the same net effect in a number of ways. If you need to achieve a very specific look and feel, however, your decisions may already be made for you—and achieving the same look and feel across browsers is exactly where the (lack of) fun begins.

Dojo attempts to normalize the differences in calculating various facets of the box model by exposing the dojo.boxModel attribute, which can take on a value of "content-box" or "margin-box" as well as the dojo.marginBox property and dojo.contentBox function, which can be used to retrieve the coordinates for the boxes. By default, dojo.boxModel is set to "content-box". In all cases, the box parameters provided in the following table refer to an Object containing values for width and height, along with an upper-left coordinate that defines the box's area. A sample margin box would look something like { l: 50, t: 200, w: 300: h: 150 } for a node offset from its parent 50px to the left, 200px from the top with a margin width of 300px, and a margin-height of 150px.

To try it out for yourself, copy the following example into a local file and open it up in Firefox:

<body style="margin:3px">
      <div id="foo" style="width:4px; height:4px; border:solid 1px;"></div>
</body>

Here's some sample output you'd see in Firebug if you copied over the page and experimented with it, and Figure 2-3 shows what it would look like in the browser:

console.log("box model", dojo.boxModel); // content-box
console.log("content box", dojo.contentBox("foo")); // l=0 t=0 w=4 h=4
console.log("margin box", dojo.marginBox("foo")); // l=3 t=3 w=6 h=6
The sample page in the browser

Figure 2-3. The sample page in the browser

Like other functions you've seen in this chapter, calling the functions with only one parameter corresponding to a node returns a value, while calling it with an additional second parameter sets the value for the node. Table 2-5 lists all the properties for working with the box model.

Table 2-5. Box model properties

Name

Return type

Comment

dojo.marginBox(/*DomNode|String*/node, /*Object?*/box)

Object

Returns an Object containing the margin box for a node.

dojo.contentBox(/*DomNode|String*/node, /*Object?*/box)

Object

Returns an Object containing the content box for a node.

dojo.coords(/*HTMLElement*/node, /*Boolean*/includeScroll)

Object

Returns margin box data for a node, including absolute positioning data. In addition to the t, l, w, and h values, additional x and y values indicate the absolute position of the element on the page; these values are offset relative to the viewport if includeScroll is set to true. dojo.coords does not act as a setter.

Tip

Dijit uses the box model facilities extensively to produce portable widgets across browsers.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.