文档结构和遍历
作为节点树的文档
Document 对象、Element 对象和文档中表示文本的 Text 对象都是 Node 对象,Node 定义了以下主要属性:
- parentNode:父节点
- childNodes:子节点(NodeList对象)
- firstChild、lastChild:第一个/最后一个子节点
- nextSibling、previousSibling:兄弟节点的前一个/后一个
- nodeType:节点类型,9 代表 Document 节点,1 代表 Element 节点,3 代表 Text 节点,8 代表 Comment 节点,11 代表 DocumentFragment 节点
- nodeValue:Text节点或Comment节点的文本内容
- nodeName:元素的标签名,以大写形式表示
使用这些 Node 属性,可以用以下类似的表达式得到文档的第一个子节点下面的第二个子节点的引用:
document.childNodes[0].childNodes[1];
document.firstChild.firstChild.nextSibling;
作为元素树的文档
将主要的兴趣点集中在文档中的元素上而非他们之间的文本上时,我们可以使用另外一个更有用的 API,它将文档看作是 Element 对象树,忽略部分文档:Text 和 Comment 节点。
该 API 的第一部分是 Element 对象的 children
属性,类似 childNodes,也是一个 NodeList 对象,但是只包含 Element 对象;另一部分是 Element 属性,后者类似 Node 对象的子属性和兄弟属性:
- firstElementChild、lastElementChild:类似 firstChild 和 lastChild,只代表子 Element
- nextElementSibling、previousElementSibling:类似 nextSibling 和 previousSibling,只代表兄弟 Element
- childElementCount:子元素的数量,和
children.length
值相等
由于遍历所有元素的 API 并未完全标准化,所以我们可以自定义遍历函数来实现:
/**
* 返回元素e的第n层祖先元素
* @param e
* @param n
* @returns {*}
*/
function parent(e, n) {
if (n === undefined)
n = 1;
while (n-- && e) {
e = e.parentNode;
}
if (!e || e.nodeType !== 1) {
return null;
}
return e;
}
/**
* 返回元素e的第n个兄弟元素
* @param e
* @param n
* @returns {*}
*/
function sibling(e, n) {
while(e && n !== 0) {
if (n > 0){
if (e.nextElementSibling)
e = e.nextElementSibling;
else
for (e=e.nextSibling; e && e.nodeType !== 1; e=e.nextSibling)
/* 空循环 */;
n--;
} else {
if (e.previousElementSibling)
e = e.previousElementSibling;
else
for (e=e.previousSibling; e && e.nodeType !== 1; e=e.previousSibling)
/* 空循环 */;
n++;
}
}
return e;
}
/**
* 返回元素n的第n代子元素
* @param e
* @param n
* @returns {*}
*/
function child(e, n) {
if (e.children) {
if (n < 0)
n += e.children.length;
if (n < 0)
return null;
return e.children[n];
}
if (n >= 0) {
if (e.firstElementChild)
e = e.firstElementChild;
else
for (e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling)
/* 空循环 */;
return sibling(e, n);
} else {
if (e.lastElementChild)
e = e.lastElementChild;
else
for (e = e.lastChild; e && e.nodeType !== 1; e = e.previousSibling)
/* 空循环 */;
return sibling(e, n+1);
}
}
无评论