目录
遍历
“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型: NodeIterator和 TreeWalker。这两个类型能够基于给定的起点对 DOM 结构执行深度优先(depth-first)的遍历操作。在与 DOM 兼容的浏览器中(Firefox 1 及更高版本、 Safari 1.3 及更高版本、 Opera 7.6 及更高版本、 Chrome0.2 及更高版本),都可以访问到这些类型的对象。 IE 不支持 DOM 遍历。
var supportsTraversals = document.implementation.hasFeature("Traversal", "2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p><b>Hello</b> world!</p>
</body>
</html>
![](https://i-blog.csdnimg.cn/blog_migrate/d431fcfa195cda5f6b01c0f04f8789ac.png)
NodeIterator
NodeIterator 类型是两者中比较简单的一个,可以使用 document.createNodeIterator()方法创建它的新实例。这个方法接受下列 4 个参数。
root:想要作为搜索起点的树中的节点。
whatToShow:表示要访问哪些节点的数字代码。whatToShow 参数是一个位掩码,通过应用一或多个过滤器(filter)来确定要访问哪些节点。
filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
//创建一个只显示<p>元素的节点迭代器
var filter = {
acceptNode: function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
}
};
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,filter, false);
//第三个参数也可以是一个与 acceptNode()方法类似的函数
var filter = function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,filter, false);
//创建一个能够访问所有类型节点的简单的 NodeIterator
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL,null, false);
NodeIterator 类型的两个主要方法是 nextNode()和 previousNode()。顾名思义,在深度优先的 DOM 子树遍历中, nextNode()方法用于向前前进一步,而 previousNode()用于向后后退一步。
<div id="div1">
<p><b>Hello</b> world!</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
</div>
//遍历<div>元素中的所有元素
var div = document.getElementById("div1");
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT,null, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
//只返回遍历中遇到的<li>元素
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT,filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
TreeWalker
TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()和 previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。
parentNode():遍历到当前节点的父节点;
firstChild():遍历到当前节点的第一个子节点;
lastChild():遍历到当前节点的最后一个子节点;
nextSibling():遍历到当前节点的下一个同辈节点;
previousSibling():遍历到当前节点的上一个同辈节点
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li"?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var walker= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT,filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
//使用 TreeWalker遍历 DOM 树,即使不定义过滤器,也可以取得所有<li>元素
var div = document.getElementById("div1");
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false);
walker.firstChild(); //转到<p>
walker.nextSibling(); //转到<ul>
var node = walker.firstChild(); //转到第一个<li>
while (node !== null) {
alert(node.tagName);
node = walker.nextSibling();
}
范围
DOM中的范围
用 DOM 范围实现简单选择
<!DOCTYPE html>
<html>
<body>
<p id="p1"><b>Hello</b> world!</p>
</body>
</html>
var range1 = document.createRange();
range2 = document.createRange();
p1 = document.getElementById("p1");
range1.selectNode(p1);//选择整个节点,包括其子节点
range2.selectNodeContents(p1);//只选择节点的子节点
用 DOM 范围实现复杂选择
var range1 = document.createRange();
range2 = document.createRange();
p1 = document.getElementById("p1");
p1Index = -1;
i, len;
for (i=0, len=p1.parentNode.childNodes.length; i < len; i++) {
if (p1.parentNode.childNodes[i] == p1) {
p1Index = i;
break;
}
}
range1.setStart(p1.parentNode, p1Index);
range1.setEnd(p1.parentNode, p1Index + 1);
range2.setStart(p1, 0);
range2.setEnd(p1, p1.childNodes.length);
操作 DOM 范围中的内容
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
range.deleteContents();
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.extractContents();
p1.parentNode.appendChild(fragment);
//使用 cloneContents()创建范围对象的一个副本,然后在文档的其他地方插入该副本。
var p1 = document.getElementById("p1"),
helloNode = p1.firstChild.firstChild,
worldNode = p1.lastChild,
range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.cloneContents();
p1.parentNode.appendChild(fragment);
插入 DOM 范围中的内容
<span style="color: red">Inserted text</span>
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(document.createTextNode("Inserted text"));
range.insertNode(span);
<p id="p1"><b>He<span style="color: red">Inserted text</span>llo</b> world</p>
折叠 DOM 范围
指范围中未选择文档的任何部分。可以用文本框来描述折叠范围的过程。假设文本框中有一行文本,你用鼠标选择了其中一个完整的单词。然后,你单击鼠标左键,选区消失,而光标则落在了其中两个字母之间。同样,在折叠范围时,其位置会落在文档中的两个部分之间,可能是范围选区的开始位置,也可能是结束位置。
比较 DOM 范围
在有多个范围的情况下,可以使用 compareBoundaryPoints()方法来确定这些范围是否有公共的边界(起点或终点)。这个方法接受两个参数:表示比较方式的常量值和要比较的范围。表示比较方式的常量值如下所示。
Range.START_TO_START(0):比较第一个范围和第二个范围的起点;
Range.START_TO_END(1):比较第一个范围的起点和第二个范围的终点;
Range.END_TO_END(2):比较第一个范围和第二个范围的终点;
Range.END_TO_START(3):比较第一个范围的终点和第一个范围的起点。
var range1 = document.createRange();
var range2 = document.createRange();
var p1 = document.getElementById("p1");
range1.selectNodeContents(p1);
range2.selectNodeContents(p1);
range2.setEndBefore(p1.lastChild);
alert(range1.compareBoundaryPoints(Range.START_TO_START, range2)); //0
alert(range1.compareBoundaryPoints(Range.END_TO_END, range2)); //1
复制 DOM 范围
var newRange = range.cloneRange();
清理 DOM 范围
range.detach(); //从文档中分离
range = null; //解除引用