[常用的遍历节点方法]
取得匹配元素的所有子元素组成的集合: children(). 该方法只考虑子元素而不考虑任何后代元素.
取得匹配元素后面紧邻的同辈元素的集合(但集合中只有一个元素): next()
取得匹配元素前面紧邻的同辈元素的集合(但集合中只有一个元素): prev()
取得匹配元素前后所有的同辈元素: siblings()
查找相关元素的前一个兄弟元素的函数
elem = elem.previousSibling;
查找相关元素的下一个兄弟元素的函数
elem = elem.nextSibling;
查找元素第一个子元素的函数
elem = elem.firstChild;
查找元素最后一个子元素的函数
elem = elem.lastChild;
返回当前元素的父元素,类似parentNode元素属性。你可以一次用一个数字来操纵多个父元素,例如parent(elem,2)就等同于parent(parent(elem))。
查找元素父元素的函数
elem.parentNode;
代码清单5-2 XML文档空格bug的补救方案
function cleanWhitespace( element ) {
// 如果不提供参数,则处理整个HTML文档
element = element || document;
// 使用第一个子节点作为开始指针
var cur = element.firstChild;
// 一直到没有子节点为止
while ( cur != null ) {
// 如果节点是文本节点,并且只包含空格
if ( cur.nodeType == 3 && ! /\S/.test(cur.nodeValue) ) {
// 删除这个文本节点
element.removeChild( cur );
// 否则,它就是一个元素
} else if ( cur.nodeType == 1 ) {
// 递归整个文档
cleanWhitespace( cur );
}
cur = cur.nextSibling; // 遍历子节点
}
}
假设你要在前面例子中使用这个函数来查找位于<h1>元素后的元素。那么代码应该类似这样:
cleanWhitespace();
// 查找H1元素
document.documentElement
.firstChild // 查找Head元素
.nextSibling // 查找<body>元素
.firstChild // 得到H1元素
.nextSibling // 得到相邻的段落(p)
该技术有优点也有缺点。最大的优点在于,你可以保证DOM文档的遍历在一定程度上的稳定性。但明显性能太差,想想必须遍历每个DOM元素和文本节点,目的只是为了找出包含空格的文本节点。假设你有一个包含大量内容的文档,它可能会严重降低网站的加载速度。此外,每次为文档注入新的HTML,你都需要重新扫描DOM中的新内容,确保没有增加新的有空格填充的文本节点。
此外此函数重要的方面是节点类型的使用。节点的类型可以由检查它的nodeType属性来确定。可能会出现好几种值,但你经常会碰到的是以下3个:
l 元素(nodeType=1):匹配XML文件中的大部分元素。比如,<li>、<a>、<p>和<body>元素都有一个值为1的nodeType。
l 文本(nodeType=3):匹配文档内的所有文本块。当使用previousSibling和nextSibling来遍历DOM结构时,你会经常碰到元素内和元素间的文本块。
l 文档(nodeType=9):匹配文档的根元素。比如,在HTML文档内,它是<html>元素。
此外,你可以用常量来表明不同的DOM节点类型(但只是对非IE浏览器有用)。与其去记住1、3或9,还不如直接直观地使用document.ELEMENT_NODE、document.TEXT_NODE或者document.DOCUMENT_NODE。因为经常清空DOM的空格会让人感到厌烦,所以你应该探索其他遍历DOM结构更有效的方法。
5.2.2 简单的DOM遍历
可以使用纯粹的DOM遍历规则(每个遍历方向都有指针)来开发一些更适合你的HTMLDOM文档遍历函数。大部分Web开发者在大多数情况下仅仅需要遍历DOM元素而非相邻的文本节点,该规则就是基于这样的事实而制定的。以下一系列的辅助函数可以帮助你,它们能够取代标准的previousSibling、nextSibling、firstChild、lastChild和parentNode。代码清单5-3展示的函数,返回的是当前元素的前一个元素,如果前一个元素不存在则是null,类似于元素的previousSibling属性。
代码清单5-3 查找相关元素的前一个兄弟元素的函数
function prev( elem ) {
do {
elem = elem.previousSibling;
} while ( elem && elem.nodeType != 1 );
return elem;
}
代码清单5-4展示的函数,返回的是当前元素的下一个元素,如果下一个元素不存在则是null,类似于元素的nextSibling属性。
代码清单5-4 查找相关元素的下一个兄弟元素的函数
function next( elem ) {
do {
elem = elem.nextSibling;
} while ( elem && elem.nodeType != 1 );
return elem;
}
代码清单5-5展示的函数,返回的是当前元素的第一个子元素,类似于firstChild元素属性。
代码清单5-5 查找元素第一个子元素的函数
function first( elem ) {
elem = elem.firstChild;
return elem && elem.nodeType != 1 ?
next ( elem ) : elem;
}
代码清单5-6展示的函数,返回的是当前元素的最后一个子元素,类似lastChild元素属性。
代码清单5-6 查找元素最后一个子元素的函数
function last( elem ) {
elem = elem.lastChild;
return elem && elem.nodeType != 1 ?
prev ( elem ) : elem;
}
代码清单5-7展示的函数,返回当前元素的父元素,类似parentNode元素属性。你可以一次用一个数字来操纵多个父元素,例如parent(elem,2)就等同于parent(parent(elem))。
代码清单5-7 查找元素父元素的函数
function parent( elem, num ) {
num = num || 1;
for ( var i = 0; i < num; i++ )
if ( elem != null ) elem = elem.parentNode;
return elem;
}
使用这些新函数你就可以迅速遍历DOM文档了,而且不必再为元素间的文本操心。比如,需要查找<h1>元素的下一个元素,现在可以这么做了:
// 查找<h1>元素的下一个元素
next( first( document.body ) )
在这段代码内有两件事值得注意。首先,此处有一个新的引用:document.body。所有的现代浏览器都在HTML DOM文档内通过body属性提供一个对<body>元素的引用。你可以用它来简化你的代码。另一件事情你可能已经注意到,那就是这些函数的书写方式十分违反直觉。一般来说,在遍历时会想:“从<body>元素开始,获取第一个元素,然后获取下一个元素”。但从我们实际编写方式来看,它是按相反的顺序。要解决这个问题,我们将讨论一些可以让自定义的遍历代码更清晰的方式。