Range对象介绍
在web中有个不常用,但挺强大的一个对象Range。他表示一个包含节点与文本节点的一部分的文档片段。
1、创建Range实例
有三种办法可以得到一个Range, 一旦一个 Range 对象被建立,在使用他的大多数方法之前需要去设置他的临界点
- 通过 Range() 构造函数来得到Range, 新创建的对象属于全局 Document 对象
- 通过 Document.createRange 方法创建 Range
- 通过 Selection 对象来获取range
下面我们来一一介绍,介绍例子时候,使用一份html文档
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<p>第一段文本</p>
<p>第二段文本</p>
<p>第三段文本</p>
<p>第四段文本</p>
<p>第五段文本在firefox中可以按住 ctr/command (windows/mac)键批量选中</p>
</div>
<div class="container">
<input type="button" value="click me" onclick="showSelection()"/>
<p>下面区域显示用鼠标选择的内容</p>
<div id="show-selection">
</div>
</div>
</body>
<script>
const eleList = document.querySelectorAll('p');
</script>
</html>
1.1、Range() 构造函数
// 使用Range构造函数新建Range实例range
const range = new Range();
// Range 起始位置在段落2之前
range.setStartBefore(eleList[1]);
// Range 起始位置在段落2之后,在这里相当于第三段之前
// range.setStartAfter(eleList[1]);
// Range 结束位置在段落3之后
// range.setEndAfter(eleList[2]);
// Range 结束位置在段落3之前,在这里相当于第二段之后
range.setEndBefore(eleList[2]);
// 以上本质上都是为了设置range的起点/终点.
// 获取 selection 对象
const selection = window.getSelection();
// 添加光标选择的范围
selection.addRange(range);
我们可以看到效果如下:
上述的第二段文本会被选中
1.2、Document.createRange
const range2 = document.createRange();
// range.setStart(startNode, startOffset);
// startNode 用于设定 Range的起始位置, startOffset 必须为不小于0的整数。表示从startNode的开始位置算起的偏移量。
// 设置range的起点
range2.setStart(eleList[1], 0);
// 设置range的终点,在这里是第三段话的第三个汉子
range2.setEnd(eleList[2].firstChild, 3)
selection.addRange(range2);
// 获取 selection 对象
const selection = window.getSelection();
效果如下图所示:
1.3、通过 Selection 对象来获取range
const showSelection = ()=>{
const ele = document.getElementById('show-selection');
const selection = document.getSelection();
const selectLen = selection.rangeCount;
if(selectLen > 0){
let html = `你选取了${selectLen}段内容:<br />`;
for(let i=0; i< selectLen; i++){
const range = selection.getRangeAt(i);
console.log(range);
html += `第${i+1}内容: ${range} <br />`;
}
ele.innerHTML = html;
}
}
点击上述按钮,效果如下所示:
在firefox浏览器中还可以按住command键选中多个文本区域
2、常用的range方法
2.1 设置range的起点和终点
如上所述,一旦一个 Range 对象被建立,在使用他的大多数方法之前需要去设置他的临界点。主要有如下的方法
- range.setStart(startNode, startOffset); startNode 用于设定 Range的起始位置。startOffset 必须为不小于0的整数。表示从startNode的开始位置算起的偏移量。
- range.setStartBefore 以其它节点为基准,设置 Range 的起点,range的开始点在该节点之前
- range.setStartAfter 以其它节点为基准,设置 Range 的起点,range的开始点在该节点之后
- range.setEndBefore 以其它节点为基准,设置 Range 的终点,range的开始点在该节点之前
- range.setEndAfter 以其它节点为基准,设置 Range 的终点,range的开始点在该节点之后 注意:使用后面四个方法设置的“起点”或“结束点”的父节点与referenceNode的父节点是同一个元素。
2.2 设置Range为Node节点及其内容
Range.selectNode() 方法将 Range 设置为包含整个 Node 节点及其内容。Range 的起始和结束节点的父节点与 referenceNode 的父节点相同
// selectNode
var selectRange = document.createRange();
var referenceNode = document.getElementsByTagName("div").item(0);
selectRange.selectNode(referenceNode);
console.log('设置Range的范围为选中的节点及其内容:', selectRange);
2.3 设置Range范围包含节点内容
Range.selectNodeContents() 方法用于设置 Range,使其包含一个 Node 的内容。
Range 的起始和结束节点的父节点即为引用节点。 startOffset 为 0, endOffset 则是引用节点包含的字符数或子节点个数。
// selectNodeContents
var selectContentRange = document.createRange();
referenceNode = document.getElementsByTagName("div")[0];
selectContentRange.selectNodeContents(referenceNode);
console.log('设置Range的范围为选中的节点:', selectRange);
2.4 克隆Range对象
Range.cloneRange()方法返回一个range对象,并且该对象的范围边界点与被克隆的range对象相同。
克隆的对象是复制过来的,而非引用,所以这两个对象双方各自做出的改变,都不会影响另一方。
const sourceRange = range.cloneRange();
sourceRange.selectNode(document.getElementsByTagName("div").item(0));
const cloneRange = sourceRange.cloneRange();
console.log('clone的range: ', cloneRange);
2.5 clone节点中的文档片段
Range.cloneContents() 返回一个 DocumentFragment,它是 Range 中所有的 Node 对象的副本。
使用" DOM 事件"添加的“事件侦听器”在克隆过程中不会被复制。 HTML属性事件与“DOM Core cloneNode”方法一样被复制。“HTML id属性”也将被克隆,这可能导致通过克隆导致无效的文档。
// cloneContents
const cloneContentsRange = range.cloneRange();
cloneContentsRange.selectNode(document.getElementsByTagName("div").item(0));
const documentFragment = cloneContentsRange.cloneContents();
console.log('cloneContents: ', documentFragment);
这里需要注意:cloneRange返回的是一个Range对象,cloneContent返回的是一个HTML片段。
2.6 插入节点
Range.insertNode() 是在Range的起始位置插入节点的方法。
新节点是插入在 the Range起始位置。如果将新节点添加到一个文本 节点, 则该节点在插入点处被拆分,插入发生在两个文本节点之间
// insertNode
const insertRange = document.createRange();
const newNode = document.createElement("p");
newNode.appendChild(document.createTextNode("在这里插入一个新的文本节点..."));
insertRange.selectNode(document.getElementsByTagName("div").item(0));
insertRange.insertNode(newNode);
2.7 移除Range至DocumentFragment中
Range.extractContents() 方法移动了Range 中的内容从文档树到DocumentFragment(文档片段对象)。
使用DOM事件添加的事件侦听器在提取期间不会保留。 HTML属性事件将按Node.cloneNode()方法的原样保留或复制。 HTML id属性也会被克隆,如果提取了部分选定的节点并将其附加到文档中,则可能导致无效的文档。
克隆了部分选定的节点,以包括使文档片段有效所需的父标记。
// extractContents
var extractRange = document.createRange();
extractRange.selectNode(document.getElementsByTagName("div").item(0));
var extractDocumentFragment = extractRange.extractContents();
console.log(extractDocumentFragment);
2.8 删除Range
Range.deleteContents() 移除来自 Document的Range 内容。 不像Range.extractContents一样,本方法不会返回一个包含删除内容的文本片段
// deleteContents
const delRange = document.createRange();
delRange.selectNode(document.getElementsByTagName("div").item(0));
delRange.deleteContents();
这样就会吧所选Range的div区域给删除了
3、总结
一个虽不常用,但很强大的API,在某些场景,比如只需要在局部渲染的时候就可以使用Range来实现。 贴一下所用到的测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<p>第一段文本</p>
<p>第二段文本</p>
<p>第三段文本</p>
<p>第四段文本</p>
<p>第五段文本在firefox中可以按住 ctr/command (windows/mac)键批量选中</p>
</div>
<div class="container">
<input type="button" value="click me" onclick="showSelection()"/>
<p>下面区域显示用鼠标选择的内容</p>
<div id="show-selection">
</div>
</div>
</body>
<script>
// 一旦一个 Range 对象被建立,在使用他的大多数方法之前需要去设置他的临界点。
const eleList = document.querySelectorAll('p');
// 1、通过 Range() 构造函数来得到Range, 新创建的对象属于全局 Document 对象
const range = new Range();
// Range 起始位置在段落2之前
range.setStartBefore(eleList[1]);
// // Range 起始位置在段落2之后,在这里相当于第三段之前
// range.setStartAfter(eleList[1]);
// Range 结束位置在段落3之后
// range.setEndAfter(eleList[2]);
// Range 结束位置在段落3之前,在这里相当于第二段之后
range.setEndBefore(eleList[2]);
// 获取 selection 对象
const selection = window.getSelection();
// 添加光标选择的范围
// selection.addRange(range);
// 2、通过 Document.createRange 方法创建 Range
const range2 = document.createRange();
// range.setStart(startNode, startOffset);
// startNode 用于设定 Range的起始位置, startOffset 必须为不小于0的整数。表示从startNode的开始位置算起的偏移量。
// 设置range的起点
range2.setStart(eleList[1], 0);
// 设置range的终点,在这里是第三段话的第三个汉子
range2.setEnd(eleList[2].firstChild, 3)
selection.addRange(range2);
// 3、通过 Selection 对象来获取range
const showSelection = ()=>{
const ele = document.getElementById('show-selection');
const selection = document.getSelection();
const selectLen = selection.rangeCount;
if(selectLen > 0){
let html = `你选取了${selectLen}段内容:<br />`;
for(let i=0; i< selectLen; i++){
const range = selection.getRangeAt(i);
console.log(range);
html += `第${i+1}内容: ${range} <br />`;
}
ele.innerHTML = html;
}
}
// selectNode
var selectRange = document.createRange();
var referenceNode = document.getElementsByTagName("div").item(0);
selectRange.selectNode(referenceNode);
console.log('设置Range的范围为选中的节点及其内容:', selectRange);
// selectNodeContents
var selectContentRange = document.createRange();
referenceNode = document.getElementsByTagName("div")[0];
selectContentRange.selectNodeContents(referenceNode);
console.log('设置Range的范围为选中的节点:', selectRange);
// clone
const sourceRange = range.cloneRange();
sourceRange.selectNode(document.getElementsByTagName("div").item(0));
const cloneRange = sourceRange.cloneRange();
console.log('clone的range: ', cloneRange);
// cloneContents
const cloneContentsRange = range.cloneRange();
cloneContentsRange.selectNode(document.getElementsByTagName("div").item(0));
const documentFragment = cloneContentsRange.cloneContents();
console.log('cloneContents: ', documentFragment);
// insertNode
const insertRange = document.createRange();
const newNode = document.createElement("p");
newNode.appendChild(document.createTextNode("在这里插入一个新的文本节点..."));
insertRange.selectNode(document.getElementsByTagName("div").item(0));
insertRange.insertNode(newNode);
// extractContents
var extractRange = document.createRange();
extractRange.selectNode(document.getElementsByTagName("div").item(0));
var extractDocumentFragment = extractRange.extractContents();
console.log(extractDocumentFragment);
// deleteContents
const delRange = document.createRange();
delRange.selectNode(document.getElementsByTagName("div").item(0));
delRange.deleteContents();
</script>
</html>```
转载自:https://juejin.cn/post/7255628830130487352