当前位置

网站首页> 程序设计 > 开源项目 > 程序开发 > 浏览文章

JavaScript DOM2和DOM3——“范围”的注意要点 - 前端学习笔记

作者:小梦 来源: 网络 时间: 2024-06-15 阅读:

“DOM2级遍历和范围”模块定义了“范围”接口。通过范围可以选择文档中的一个区域,而不必考虑节点的界限(选择在后台完成,对用户是不可见的)。

DOM中的范围

DOM2级在Document类型中定义了createRange()方法,可以用来创建DOM范围,如下所示:

var range = document.createRange();

每个范围由一个Range类型的实例表示,这个实例有很多属性和方法:

  • startContainer:包含范围起点的节点(选区中第一个节点的父节点);

  • startOffset:范围在startContainer中起点的偏移量;

  • endContainer:包含范围终点的节点(选区中最后一个节点的父节点);

  • endOffset:范围在endContainer中终点的偏移量;

  • commonAncestorContainer:startContainer和endContainer共同的祖先节点在文档树中位置最深的那个;

用DOM范围实现简单选择

selectNode()selectNodeContents()

前者选择整个节点,包括子节点;后者选择节点的子节点。如:

<body>    <p id="p1"><b>Hello</b> world!</p>    <script>    var range1 = document.createRange(),        range2 = document.createRange(),        p1 = document.getElementById("p1");    range1.selectNode(p1);    range2.selectNodeContents(p1);        console.log(range1.startContainer); //document.body    console.log(range1.startOffset); //1 有个空格    console.log(range1.endContainer); //document.body    console.log(range1.endOffset); //2    console.log(range1.commonAncestorContainer); //document.body        console.log(range2.startContainer); //p1    console.log(range2.startOffset); //0    console.log(range2.endContainer); //p1    console.log(range2.endOffset); //2    console.log(range2.commonAncestorContainer); //p1    </script></body>

在调用selectNode()时,startContainer、endContainer和commonAncestorContainer等都等于传入节点的父节点,也就是其中的document.body。而startOffset属性等于给定节点在其父节点的childNodes集合中的索引。endOffset等于startOffset加上1;

在调用selectNodeContents()时,startContainer、endContainer和commonAncestorContainer等于传入的节点。而startOffset属性始终等于0.最后,endOffset等于子节点的数量(node.childNodes.length);

更精细的选择

为了更精细的控制将哪些节点包含在范围中,还可以使用下列方法:

  • setStartBefore(refNode):将范围的起点设置在refNode之前;

  • setStartAfter(refNode):将范围的起点设置在refNode之后;

  • setEndBefore(refNode):将范围的终点设置在refNode之前;

  • setEndAfter(refNode):将范围的终点设置在refNode之后;

如下html:

<p id="p1">    <b>Hello</b> world!</p><div>    <p>hello</p></div>

js:

var range1 = document.createRange(),    range2 = document.createRange(),    p1 = document.getElementById("p1");range1.selectNode(p1);range1.setStartAfter(p1);console.log(range1.startContainer); //document.bodyconsole.log(range1.startOffset); //2 有1个空格和一个p元素console.log(range1.endContainer); //document.bodyconsole.log(range1.endOffset); //2 选择了一个元素,所以是startOffset加1console.log(range1.commonAncestorContainer); //document.bodyrange2.setStartAfter(p1); //结果与上面的相同console.log(range2.startContainer); //document.bodyconsole.log(range2.startOffset); //2console.log(range2.endContainer); //document.bodyconsole.log(range2.endOffset); //2console.log(range2.commonAncestorContainer); //document.body

用DOM范围实现更加复杂的选择

setStart()setEnd()方法

这两个方法都接受两个参数:一个参照节点和一个偏移量值。对前者来说,参照节点会变成startContainer,偏移值则会变成startOffset。对于后者来说,参照节点会变成endContainer,偏移值会变成endOffset。

html:

<p id="p1"><b>Hello</b> world!</p>

js:

var p1 = document.getElementById("p1"),    helloNode = p1.firstChild.firstChild,    worldNode = p1.lastChild;var range = document.createRange();range.setStart(helloNode, 2);range.setEnd(worldNode, 3);

这样就完成了对“llo wo”的选择,但仅仅完成对该选区的选择意义不大,重要的是对其进行操作。

操作DOM范围中的内容

deleteContents()删除范围所包含的内容

如:

<p id="p1"><b>Hello</b> world!</p>

举例:

var p1 = document.getElementById("p1");var hello = p1.firstChild.firstChild;var world = p1.lastChild;var range = document.createRange();range.setStart(hello, 1);range.setEnd(world, 2);console.log(range.toString()); //ello wconsole.log(p1.outerHTML); //<p id="p1"><b>Hello</b> world!</p>range.deleteContents(); //刪除範圍內的內容console.log(p1.outerHTML); //<p id="p1"><b>H</b>orld!</p> 

又如:

<ul id="list">    <li>thi is a list No.1</li>    <li>thi is a list No.2</li>    <li>thi is a list No.3</li>    <li>thi is a list No.4</li>    <li>thi is a list No.5</li></ul>

举例:

var list = document.getElementById("list");var starting = list.getElementsByTagName("li")[1];var ending = list.getElementsByTagName("li")[3];var range = document.createRange();range.setStart(starting, 0);range.setEnd(ending, 0);console.log(range.toString());console.log(list.outerHTML);range.deleteContents();console.log(list.outerHTML);

extractContents()移除范围所包含的内容并返回文档片段

如:

<p id="p1"><b>Hello</b> world!</p>

举例:

var p1 = document.querySelector("#p1");var helloNode = p1.firstChild.firstChild,    worldNode = p1.lastChild;var range = document.createRange();range.setStart(helloNode, 2);range.setEnd(worldNode, 3);var fragment = range.extractContents();p1.parentNode.insertBefore(fragment, p1);

又如:

<ul id="list">    <li>thi is a list No.1</li>    <li>thi is a list No.2</li>    <li>thi is a list No.3</li>    <li>thi is a list No.4</li>    <li>thi is a list No.5</li></ul>

举例:

var list = document.getElementById("list");var range = document.createRange();var starting = list.getElementsByTagName("li")[1];var ending = list.getElementsByTagName("li")[4];range.selectNode(list);range.setStartBefore(starting);range.setEndBefore(ending);var fragment = range.extractContents();document.body.appendChild(fragment);console.log(document.body.innerHTML);// <li>thi is a list No.2</li>// <li>thi is a list No.3</li>// <li>thi is a list No.4</li>console.log(list.innerHTML);// <li>thi is a list No.1</li>// <li>thi is a list No.5</li>

cloneContents()创建范围对象的一个副本

如:

var list = document.getElementById("list");var range = document.createRange();var starting = list.getElementsByTagName("li")[1];var ending = list.getElementsByTagName("li")[4];range.selectNode(list);range.setStartBefore(starting);range.setEndBefore(ending);var fragment = range.cloneContents();document.body.appendChild(fragment);console.log(document.body.innerHTML);// <li>thi is a list No.2</li>// <li>thi is a list No.3</li>// <li>thi is a list No.4</li>console.log(list.innerHTML);// <li>thi is a list No.1</li>// <li>thi is a list No.2</li>// <li>thi is a list No.3</li>// <li>thi is a list No.4</li>// <li>thi is a list No.5</li>

插入DOM范围中的内容

insertNode()向范围选区的开始处插入一个节点(范围内部插入内容)

如:

<ul id="list">    <li>thi is a list No.1</li>    <li>thi is a list No.2</li>    <li>thi is a list No.3</li>    <li>thi is a list No.4</li>    <li>thi is a list No.5</li></ul>

代码:

<script>var list = document.getElementById("list");var range = document.createRange();var starting = list.getElementsByTagName("li")[1];var ending = list.getElementsByTagName("li")[4];range.selectNode(list);range.setStartBefore(starting);range.setEndBefore(ending);var newLi = document.createElement("li");newLi.appendChild(document.createTextNode("data"));range.insertNode(newLi);console.log(list.innerHTML);// <li>thi is a list No.1</li>// <li>data</li><li>thi is a list No.2</li>// <li>thi is a list No.3</li>// <li>thi is a list No.4</li>// <li>thi is a list No.5</li></script>

又如:

<p id="p1"><b>Hello</b> world!</p>

代码:

var p = document.getElementById("p1");var range = document.createRange();var starting = p.firstChild.firstChild;var ending = p.lastChild;range.setStart(starting, 5);range.setEnd(ending, 0);var span = document.createElement("span");span.appendChild(document.createTextNode(" there in this"));range.insertNode(span);console.log(p1.innerText); //Hello there in this world!console.log(p1.innerHTML); //<b>Hello<span> there in this</span></b> world!

surroundContents()向范围选区周围插入一个节点(围绕范围插入内容)

通常与selectNode()配合,因为范围必须包含整个DOM选区,不能仅仅包含选中的DOM节点。

如:

<p id="p1"><b>Hello</b> world!</p>

代码:

var p = document.getElementById("p1");var range = document.createRange();range.selectNode(p.firstChild);var span = document.createElement("span");span.style.border = "1px solid orange";span.style.borderRadius = "3px";range.surroundContents(span);console.log(p1.innerHTML); //<span style="border: 1px solid orange; border-radius: 3px;"><b>Hello</b></span> world! 

为了插入span标签,范围必须包含整个DOM选区,所以推荐使用selectNode()配合。

折叠DOM范围

collapse()方法

折叠就是指范围中未选择文档的任何部分。该函数接收一个参数,一个布尔值。true表示折叠到范围的起点,参数false表示折叠到范围的终点。要确定范围已经折叠完毕,可以检查collapsed属性:

如:

<p id="p1"><b>Hello</b> world!</p>

代码:

var p = document.getElementById("p1");var range = document.createRange();range.selectNode(p);range.collapse(true);console.log(range.collapsed); //True

又如:

<ul id="list">    <li>thi is a list No.1</li>    <li>thi is a list No.2</li>    <li>thi is a list No.3</li>    <li>thi is a list No.4</li><li>thi is a list No.5</li></ul>

代码:

var list = document.querySelector("#list");var range = document.createRange();range.setStartAfter(list.getElementsByTagName("li")[1]);range.setEndBefore(list.getElementsByTagName("li")[2]);console.log(range.collapsed); //False 因为还有一个空白节点在这里var range2 = document.createRange();range2.setStartAfter(list.getElementsByTagName("li")[3]);range2.setEndBefore(list.getElementsByTagName("li")[4]);console.log(range2.collapsed); //True 因为范围没有选中任何部分

比较DOM范围

comopareBoundaryPoints()方法来比较

该方法涌来比较这些范围是否有公共的边界。接收两个参数:表示比较方式的常量值和要比较的范围。如:

  • Range.START_TO_START - 比较两个 Range 节点的开始点

  • Range.END_TO_END - 比较两个 Range 节点的结束点

  • Range.START_TO_END - 用 sourceRange 的开始点与当前范围的结束点比较

  • Range.END_TO_START - 用 sourceRange 的结束点与当前范围的开始点比较

注意:《高级程序设计》一书中,对后两个的说明太模糊(不正确?);下面是w3school的解释:

您可能认为,首先需要用参数 how 的范围常量指定当前范围的边界点,然后再用它指定 sourceRange 的边界点。但事实上,

  1. 常量 Range.START_TO_END 指定与当前范围的 end 点和 sourceRange 的 start 点进行比较。

  2. 常量 Range.END_TO_START 指定比较当前范围的 start 点和指定范围的 end 点。

如果第一个范围中的点位于第二个范围中的点之前,返回-1;如果相等返回0;如果第一个范围中的点位于第二个范围中的点之后,返回1

如:

<p id="p1"><b>Hello</b> world!</p>

代码:

var p = document.getElementById("p1");var range1 = document.createRange();var range2 = document.createRange();range1.selectNodeContents(p);range2.selectNodeContents(p);range2.setEndBefore(p.lastChild);console.log(range1.toString()); //Hello world!console.log(range2.toString()); //Helloconsole.log(range1.compareBoundaryPoints(Range.START_TO_START, range2)); //0console.log(range1.compareBoundaryPoints(Range.END_TO_END, range2)); //1

又如:

<ul id="list">    <li>thi is a list No.1</li>    <li>thi is a list No.2</li>    <li>thi is a list No.3</li>    <li>thi is a list No.4</li><li>thi is a list No.5</li></ul>

代码:

var list = document.querySelector("#list");var range1 = document.createRange();var range2 = document.createRange();range1.selectNodeContents(list);range1.setStartAfter(list.firstChild.nextSibling);range1.setEndBefore(list.lastChild.previousSibling);range2.selectNodeContents(list);console.log(range1.compareBoundaryPoints(Range.START_TO_START, range2)); //1console.log(range1.compareBoundaryPoints(Range.END_TO_END, range2)); //-1console.log(range1.compareBoundaryPoints(Range.START_TO_END, range2)); //1 注意这里是range1的END与range2的start对比;console.log(range1.compareBoundaryPoints(Range.END_TO_START, range2)); //-1 注意这里是range1的START与range2的END对比;

复制DOM范围

cloneRange()方法复制范围;

var newRange = range.cloneRange();

清理DOM范围

detach()方法清理范围;

range.detach();range = null;

上面的是从文档中分离范围;下面的是解除引用。

下节再讨论IE8及更早版本中的范围

热点阅读

网友最爱