0.用DOM范围实现复杂选择
setStart()和setEnd()
要创建复杂的范围就得使用setStart()和setEnd()方法。这两个方法都接受两个参数:一个参照节点和一个偏移量值。
对setStart()来说,参照节点会变成startContainer。 而偏移量值会变成startoffset。对于setEnd()来说,参照节点会变成endContainer,而偏移量值会变成endOffset。 可以使用这两个方法来模仿selectNode()和selectNodeContents(),但是这没意义,它的强大之处在于能够选择节点的一部分。
var range1 = document.createRange();
var p1 = document.getElementById("p1");
var b = p1.children[0];
var span = p1.children[1];
range1.setStart(b.firstChild,1);
range1.setEnd(span.firstChild,5);
alert(range1);//ello worl
具体关于为什么最后range1显示ello worl,我在上一篇的博客中已经写了详细的原因
☞此处应有链接1.操作DOM范围中的内容
在创建范围时,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档片段中。
为了创建这个文档片段,范围内容的格式必须正确有效。 在前面的例子中,创建的选区分别开始和结束于两个文本节点的内部,因此不能算是格式良好的DOM结构,也就无法通过DOM来表示。 但是,范围知道自身缺少哪些开标签和闭标签,它能够重新构建有效的DOM结构以便对其进行操作.
对于前面的例子而言,范围经过计算知道选区中缺少一个开始的<\b>标签,因此就会在后台动态加入一个该标签, 同时还会在前面加入一个表示结束的<\/b>标签以结束"H"。于是,修改后的DOM就变成了如下所示
<\b>ello<\/b><\span> worl<\/span>
像这样创建了范围之后,就可以使用各种方法对范围的内容进行操作了 (注意!!!表示范围的内部片段中的所有节点,都只是指向文档中相应节点的指针!)。
deleteContents()方法
从文档中删除范围所包含的内容。
var range1 = document.createRange();
var p1 = document.getElementById("p1");
var b = p1.children[0];
var span = p1.children[1];
range1.setStart(b.firstChild,1);
range1.setEnd(span.firstChild,5);
alert(range1);//ello worlv
range1.deleteContents();// hd!
HTML代码显示
<\b>H<\/b><\span>d!<\/span>
由于范围选取在修改底层DOM结构时能够保证格式良好,因此即使内容被删除了,最终的DOM结构依旧是格式良好的。
extractContents()方法
和deleteContents()方法相似,也会从文档中移除范围选取,但是extractContents方法会返回范围的文档片段。 利用这个返回的值,可以将范围的内容插入到文档中的其他地方。
var fragment = range1.extractContents();
p1.parentNode.appendChild(fragment);
//hd!
//ello worl
cloneContents()
cloneContents()创建范围对象的一个副本,然后再文档的其他地方插入该副本。 cloneContents()返回的文档片段包含的是范围中节点的副本,而不是实际的节点.
var fragment = range1.cloneContents();
p1.parentNode.appendChild(fragment);
//hello world!
//ello worl
PS:在将文档片段传入appendChild()方法中时,添加到文档中的只是片段的子节点,而非片段本身。
注意!在调用上面介绍的方法之前,拆分的节点并不会产生格式良好的文档片段。换句话说,原始的HTML在DOM被修改之前会始终保持不变。
个人理解
就是在使用这些方法之前,HTML还是完整的,像这样
<\b>hello<\/b><\span> world!<\/span>
而不会变成
<\b>he<\/b><\b>llo<\/b><\span> world!<\/span>
2.插入DOM范围中的内容
insertNode()方法
向范围选区的开始处插入一个节点
惯例写一个小例子
var range1 = document.createRange();
var p1 = document.getElementById("p1");
var b = p1.children[0];
var span = p1.children[1];
range1.setStart(b.firstChild,1);
range1.setEnd(span.firstChild,5);
var sp = document.createElement("span");
sp.style.color = "red";
sp.appendChild(document.createTextNode("Inserted text"));
range1.insertNode(sp);
效果
hInserted textello world!
HTML代码显示:
<\p id="p1"><\b>h<\span style="color: red;">Inserted text<\/span>ello<\/b><\span> world!<\/span><\/p>
注意:"span"元素正好被插入到了h之后,而被拆分的hello并没有多出一对b元素来, 是因为这里没有使用我之前说的(小标题1.操作DOM范围中的内容)那些方法。
surroundContents()方法
环绕范围插入内容,这个方法接受一个参数,即环绕范围内容的节点。
- 提取出范围中的内容(类似于执行extractContent());
- 将给定节点插入到文档中原来范围所在的位置上;
- 将文档片段的内容添加到给定节点中.
例子
var range1 = document.createRange();
var p1 = document.getElementById("p1");
var b = p1.children[0];
var span = p1.children[1];
range1.setStart(b.firstChild,1);
range1.setEnd(b.firstChild,4);
var sp = document.createElement("span");
sp.style.color = "red";
range1.surroundContents(sp);
效果
hello world!
HTML显示:
<\p id="p1"><\b>h<\span style="color: red;">ell<\span>o<\/b><\span> world!<\/span><\/p>
书上说:为了插入span,范围必须包含整个DOM选区(不能仅仅包含选中的DOM节点)
并不明白这句话的意思,那就试一下呗。
经过测试,整个方法不能跨标签使用,也就是说,如果我这么写代码
range1.setStart(b.firstChild,1);
range1.setEnd(span.firstChild,4);
马上就能美滋滋的获得一个报错!
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
然后百度了一下,说是这个方法和selectNode()方法更配!
但是测试下来就算不是一个完整的标签也可以用这个surroundContents()方法呀!美滋滋的实践出真知,只是不能跨标签,可惜了。