为了能在较大尺度上控制页面,可以使用范围。范围可以用来选择文档的某个部而不管节点的边界(注意这个选取是在后台发生的,对用户不可见)。
当一般的DOM操作不足以用来改变文档时,范围将非常有用。通常,有两种不同的范围实现:一种是DOM的,另一种是IE的。
DOM Level2定义了方法createRange()来创建范围。在DOM兼容的浏览器,这个方法属于document对象,所以可以这样创建一个范围:
var oRange=document.createRange();
1、 DOM范围中的简单选区
选择使用范围的文档的某部分的最简单的方法是用selectNode()和selectNodeContents()。这两个方法都接受一个参数(DOM节点),然后它们用节点中的信息来填充范围。
其中,selectNode()方法将选择整个节点,包括它的子节点,而selectNodeContents()则选择节点中所有的子节点。例如:
<p id=”p1”><b>Hello</b> World</p>
这段代码可以用以下JavaScript代码来访问:
var oRange1=document.createRange();
var oRange2=document.createRange();
var oP1=document.getElementById(“p1”);
oRange1.selectNode(oP1);
oRange2.selectNodeContents(oP1);
这两个例子中的两个范围包含文档的不同选区:oRange1包含<p>元素及所有节节点,而oRange2包含<b/>元素及文本节点World。
创建范围时,会分配给它一些特性:
(1) startContainer——范围是从哪个节点中开始的。
(2) startOffset——在startContainer中范围开始的偏移位置。如果startContainer是个文本节点、注释节点或者是CData节点,startOffset是指范围开始前的字符数:否则,偏移是范围中第一个子节点的索引。
(3) endContainer——范围是在哪个节点中结束的。
(4) endOffset——在endContainer中范围结束的偏移位置。
(5) commonAncestorContainer——startContainer和endContainer都处于哪个最小的节点。
以上特性都是只读的,用来提供范围的额外信息。
注意:当使用selectNode()时,startContainer、endContainer和commonAncestorContainer均等同于传入的节点的父节点;startOffset等同于给定在父节点的childNodes集合中的索引,而endOffset等同于startOffset加1(因为只选择了一个节点);当使用selectNodeContents()时,startContainer、endContainer和commonAncestorContainer均等同于传入的节点;startOffset等于0,而endOffset等于节点的数量(node.childNode.legth)。
还有一些方法可以直接设置这些特性:
setStartBefore(refNode)——将范围的起点设在refNode之前(这样refNode就是选区的第一个节点)。同时,startCotainer特性被设置成refNode的父节点,startOffset特性被设置为refNode在其父节点的childNodes集合中的索引。
setStartAfter(refNode)——将范围的起点设在refNode之后(这样refNode就不是选区的一部分,而它的下一个兄弟节点则是选区的第一个节点)。同时,startCotainer特性被设置成efNode的父节点,startOffset特性被设置为refNode在其父节点的childNodes集合中的索引加一。
setEndBefore(refNode)——将范围的终点设在refNode之前(这样refNode就不是选区的一部分,而它的前一个兄弟节点则是选区的最后一个节点)。同时,endCotainer特性被设置成refNode的父节点,并将endOffset特性被设置为refNode在其父节点的childNodes集合中的索引。
setEndAfter(refNode)——将范围的终点设在refNode之前(这样refNode将成为选区的最后一个节点)。同时,endCotainer特性被设置成refNode的父节点,并将endOffset特性被设置为refNode在其父节点的childNodes集合中的索引加一。
2、 DOM范围中的复杂选区
创建复杂选区需要使用范围的setStart()和setEnd()方法。这两个方法均接受两个参数:节点的引用和偏移量。对于setStart(),引用的节点为startContainer,偏移量为startOffset;对于setEnd(),引用的节点为endContainer,而偏移量为endOffset。
在HTML代码<p id=”p1”><b>Hello</b> World</p>中,从Hello中选择llo和从world中选择wo。通过使用setStart()和setEnd(),可以很容易完成这个任务。代码如下:
var oP1=document.getElementById(“p1”);
var oHello=oP1.firstChild.firstChild;
var oWorld=oP1.lastChild;
var oRange=document.createRange();
oRange.setStart(oHello,2);
oRange.setEnd(oWorld,3);
对于setStart(),偏移量是2,因为第一个l在Hello中是在位置2(从H位置0开始)上。对于setEnd(),偏移量是3,表示第一个字符不应该被选中,这个字符是r在位置3上(在oWorld节点中有一个空格)。
3、 与DOM范围对象的内容进行交互
deleteContents():从文档中将范围内的内容全部删除。
extractContents():从文档中将范围内的内容部分删除,并将范围内的文档碎片作为函数返回值返回。例如:有DOM文档: <p><b>He</b>llo</b> World</p>
var oP1=document.getElementById(“p1”);
var oHello=oP1.firstChild.firstChild;
var oWorld=oP1.lastChild;
var oRange=document.createRange();
oRange.setStart(oHello,2);
oRange.setEnd(oWorld,3);
var oFragment=oRange.extractContents();
document.body.appendChild(oFragment);
这个例子中,碎片被取出并加到文档中<body/>元素的结尾(记住,当文档碎片传到appendChild()中时,只有碎片的子节点会被插入,而碎片本身不会被插入)。这个例子中可以看到代码<b>He</b>rld在页面的顶部,而<b>llo</b>Wo在页面的底部。
4、 插入DOM范围的内容
insertNode()方法用来在选区的开头插入节点。
surroundContents()方法插入包围范围的内容,该方法接受一个参数,在后台会执行以下几步:(1)抽取范围中的内容;(2)将给定节点插入到原来范围所在的位置;(3)将文档碎片的内容添加到给定节点中。
var oP1=document.getElementById(“p1”);
var oHello=oP1.firstChild.firstChild;
var oWorld=oP1.lastChild;
var oRange=document.createRange();
var oSpan=document.createElement(“span”);
oSpan.style.backgroundColor=”yellow”;
oRange.setStart(oHello,2);
oRange.setEnd(oWorld,3);
oRange.surroundContents(oSpan);
5、 折叠DOM范围
要清空范围(也就是,不选择文档的任何部分),可以折叠它。折叠范围类似文本框的一种行为。当文本框中有文字时,可以用鼠标突出全部文字。但是,只要再点击一下鼠标左键,选区就消失了,光标将停留在两个字母之前。折叠范围,是将范围的位置设置在文档中间,要么是原来选区的开头,要么是结尾。
Collapse()方法来折叠范围,这个方法接受一个参数:用来表示是折叠到范围的哪一端的Boolean值。如果参数是true,落雷就折叠到开头;如果是false,范围就折叠到末尾。要判断范围是否已被折叠,可以用collapsed特性:
oRange.collaspe(true);
alert(oRange.collapsed); //outputs”true”
6、 比较DOM范围
如果有几个范围,则可以使用compareBoundaryPoints()方法来判断范围是否有相同的边界(开头或者结尾)。这个方法接受两个参数:要进行比较的范围及如何进行比较,后者是个常量值:
START_START(0)——比较两个范围的起点。
START_TO_END(1)——比较第一个范围的起点和第二个范围的终点。
END_TO_END(2)——比较两个范围的终点。
END_TO_START(3)——比较第一个范围的终点和第二个范围的起点。
如果第一个范围的被测点在第二个范围的被测点之前,compareBoundaryPoints()方法返回-1;如果两个点一样,返回0;如果第一个落雷的被测点在第二个范围的被测点的后面,返回1。例如:
var oRange1=document.createRange();
var oRange2=document.createRange();
var Op1=document.getElementById(“p1”);
oRange1.selectNodeContents(oP1);
oRange2.selectNodeContents(oP1);
alert(oRange1.compareBoundaryPoints(Range.START_TO_START,oRang2)); //outpus 0
alert(oRange1.compareBoundaryPoints(Range.END_TO_END,oRange2)); outputs 1
7、 复制DOM范围
如果需要,可通过cloneRange()方法来创建范围的副本。它可以创建与被调用的范围对象完全一致的副本:
var oNewRange=oRange.cloneRange();
新的范围包含的所有特性与原范围的特性均相同,并且能够丝毫不会影响原范围地修改它。
8、清理
当用完范围后,最好用detach()方法释放系统资源。这不是必需的,因为不再被引用的范围最后总是会被垃圾收集器清理掉。然而,如果范围已用完且不再需要了,那么调用detach()方法可以保证它不会一直占用不必要的内存空间。