JavaScript: The Definitive Guide, Sixth Editio javaScript权威指南(第6版) pdf 文字版-文字版, javascript电子书, 和javascript 有关的电子书:

15.3.2 Documents As Trees of Elements

Note, however, that this API is extremely sensitive to variations in the document text. If the document is modified by inserting a single newline between theand thetag, for example, the Text node that represents that newline becomes the first child of the first child, and the second child is theelement instead of thebody.

15.3.2 Documents As Trees of Elements

When we are primarily interested in the Elements of a document instead of the text within them (and the whitespace between them), it is helpful to use an API that allows us to treat a document as a tree of Element objects, ignoring Text and Comment nodes that are also part of the document.

The first part of this API is the children property of Element objects. Like childNodes, this is a NodeList. Unlike childNodes, however, the childrenlist contains only Element objects. The children property is nonstandard, but it works in all current browsers. IE has implemented it for a long time, and most other browsers have followed suit. The last major browser to adopt it was Firefox 3.5.

Note that Text and Comment nodes cannot have children, which means that the Node.parentNodeproperty described above never returns a Text or Comment node. The parentNode of any Element will always be another Element, or, at the root of the tree, a Document or DocumentFragment.

The second part of an element-based document traversal API is Element properties that are analogs to the child and sibling properties of the Node object:

firstElementChild, lastElementChild Like firstChild and lastChild, but for Element children only. nextElementSibling, previousElementSibling Like nextSibling and previousSibling, but for Element siblings only. childElementCount The number of element children. Returns the same value as children.length.

These child and sibling properties are standardized and are implemented in all current browsers except IE.4

Because the API for element-by-element document traversal is not yet completely universal, you might want to define portable traversal functions like those in Example 15-2 .

Example 15-2. Portable document traversal functions


*Return the nth ancestor of e, or null if there is no such ancestor * or if that ancestor is not an Element (a Document or DocumentFragment e.g.).*If n is 0 return e itself. If n is 1 (or * omitted) return the parent. If n is 2, return the grandparent, etc. */

function parent(e, n) { if (n === undefined) n = 1; while(n-- && e) e = e.parentNode; if (!e || e.nodeType !== 1) return null; return e;



*Return the nth sibling element of Element e.*If n is postive return the nth next sibling element.*If n is negative, return the -nth previous sibling element.*If n is zero, return e itself. */ function sibling(e,n) { while(e && n !== 0) { // If e is not defined we just return it

if (n > 0) { // Find next element sibling if (e.nextElementSibling) e = e.nextElementSibling; else {

for(e=e.nextSibling; e && e.nodeType !== 1; e=e.nextSibling)

/* empty loop */ ; } n--;


else { // Find the previous element sibling if (e.previousElementSibing) e = e.previousElementSibling; else {

for(e=e.previousSibling; e&&e.nodeType!==1; e=e.previousSibling)

/* empty loop */ ; } n++;

4. .

15.3 Document Structure and Traversal | 373

} } return e;



*Return the nth element child of e, or null if it doesn't have one.*Negative values of n count from the end. 0 means the first child, but * -1 means the last child, -2 means the second to last, and so on. */

function child(e, n) {

if (e.children) { // If children array exists if (n < 0) n += e.children.length; // Convert negative n to array index if (n < 0) return null; // If still negative, no child return e.children[n]; // Return specified child


// If e does not have a children array, find the first child and count // forward or find the last child and count backwards from there. if (n >= 0) { // n is non-negative: count forward from the first child

// Find the first child element of e if (e.firstElementChild) e = e.firstElementChild; else {

for(e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling)

/* empty */; } return sibling(e, n); // Return the nth sibling of the first child


else { // n is negative, so count backwards from the end if (e.lastElementChild) e = e.lastElementChild; else {

for(e = e.lastChild; e && e.nodeType !== 1; e=e.previousSibling)

/* empty */; } return sibling(e, n+1); // +1 to convert child -1 to sib 0 of last

} }

Defining Custom Element Methods

All current browsers (including IE8, but not IE7 and before) implement the DOM so that types like Element and HTMLDocument5 are classes like String and Array. They are not constructors (we’ll see how to create new Element objects later in the chapter), but they have prototype objects and you can extend them with custom methods: = function() { if (this.nextElementSibling) return this.nextElementSibling; var sib = this.nextSibling; while(sib && sib.nodeType !== 1) sib = sib.nextSibling; return sib;


5. IE8 supports extendable prototypes for Element, HTMLDocument, and Text, but not for Node, Document, HTMLElement, or any of the more specific HTMLElement subtypes.

The functions of Example 15-2 are not defined as Element methods because this technique is not supported by IE7.

This ability to extend DOM types is still useful, however, if you want to implement IE-specific features in browsers other than IE. As noted above, the nonstandard Element property children was introduced by IE and has been adopted by other browsers. You can use code like this to simulate it in browsers like Firefox 3.0 that do not support it:

// Simulate the Element.children property in non-IE browsers that don't have it // Note that this returns a static array rather than a live NodeList if (!document.documentElement.children) {

Element.prototype.__defineGetter__("children", function() { var kids = []; for(var c = this.firstChild; c != null; c = c.nextSibling)

if (c.nodeType === 1) kids.push(c); return kids; }); }

The __defineGetter__ method (covered in §6.7.1 ) is completely nonstandard, but it is perfect for portability code like this.

友情链接It题库(| 版权归yishouce.com所有| 友链等可联系|粤ICP备16001685号-1