JavaScript

JavaScript 知识量:26 - 101 - 483

15.4 范围><

DOM范围- 15.4.1 -

DOM2在Document类型上定义的createRange()方法用于创建一个Range对象。Range对象表示文档中的一段范围。使用Range对象,可以精确地控制文档中需要操作的范围,实现更加灵活和高效的操作。

createRange()方法没有参数,返回一个Range对象。

Range对象可以用于多种操作,例如选择文本、剪切/粘贴文本、插入文本等。Range对象有两个重要的方法:

  1. startContainer和endContainer:这两个方法分别返回Range的起始节点和结束节点。

  2. startOffset和endOffset:这两个方法分别返回Range的起始偏移量和结束偏移量。

以下是一个使用DOM2的createRange()方法的应用示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 创建一个Range对象,范围包括第一个元素节点及其全部内容  
const range = doc.createRange();  
range.setStart(node, 0);  
range.setEnd(node, node.childNodes.length);  
  
// 输出Range的起始节点、结束节点、起始偏移量和结束偏移量  
console.log(`Start Node: ${range.startContainer}, Offset: ${range.startOffset}`);  
console.log(`End Node: ${range.endContainer}, Offset: ${range.endOffset}`);

这个示例使用DOM2的createTreeWalker()方法遍历DOM树,找到第一个元素节点。然后使用createRange()方法创建一个Range对象,范围包括第一个元素节点及其全部内容。最后输出Range的起始节点、结束节点、起始偏移量和结束偏移量。

简单选择- 15.4.2 -

要通过范围选择文档中的某个部分,最简单的方式是使用selectNode()或selectNodeContents()方法。这两个方法都接受一个参数,即一个DOM节点。

  • selectNode()方法会将传入的节点的整个节点作为范围。这意味着它将选择指定的节点,以及该节点的所有子节点。

  • selectNodeContents()方法只会将传入的节点的所有子节点作为范围。它不会包括节点本身。

这两个方法都返回一个Range对象,该对象表示文档中的一段范围。可以使用这个Range对象进行各种操作,比如选择文本、剪切/粘贴文本、插入文本等。

为了创建文档片段,范围内容的格式必须正确有效。可以使用deleteContents()方法来删除范围中的内容。

以下是一个使用selectNode()方法的应用示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 使用selectNode()方法选择第一个元素节点及其全部内容  
const range = doc.createRange();  
range.selectNode(node);  
  
// 输出Range的起始节点、结束节点、起始偏移量和结束偏移量  
console.log(`Start Node: ${range.startContainer}, Offset: ${range.startOffset}`);  
console.log(`End Node: ${range.endContainer}, Offset: ${range.endOffset}`);

在这个示例中,首先遍历DOM树,找到第一个元素节点。然后,使用selectNode()方法选择该元素节点及其全部内容。最后,输出Range的起始节点、结束节点、起始偏移量和结束偏移量。

复杂选择- 15.4.3 -

要创建复杂的范围,需要使用setStart()和setEnd()方法。这两个方法都接受两个参数:一个是一个节点引用,另一个是偏移量值。

  • setStart()方法接受一个参照节点和一个偏移量值。参照节点会变成Range对象的起始容器(startContainer),而偏移量值会变成起始偏移量(startOffset)。

  • setEnd()方法同样接受一个参照节点和一个偏移量值。参照节点会变成Range对象的结束容器(endContainer),而偏移量值会变成结束偏移量(endOffset)。

这两个方法可以用来模拟selectNode()和selectNodeContents()。通过使用setStart()和setEnd(),可以更精确地控制Range的范围,从而实现更复杂的操作。

以下是一个使用setStart()和setEnd()方法的应用示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 使用setStart()和setEnd()方法创建一个范围,包括第一个元素节点及其第二个子节点的内容  
const range = doc.createRange();  
range.setStart(node, 0);  
range.setEnd(node.childNodes[1], node.childNodes[1].childNodes.length);  
  
// 输出Range的起始节点、结束节点、起始偏移量和结束偏移量  
console.log(`Start Node: ${range.startContainer}, Offset: ${range.startOffset}`);  
console.log(`End Node: ${range.endContainer}, Offset: ${range.endOffset}`);

在这个示例中,首先遍历DOM树,找到第一个元素节点。然后,使用setStart()和setEnd()方法创建一个范围,包括第一个元素节点及其第二个子节点的内容。最后,输出Range的起始节点、结束节点、起始偏移量和结束偏移量。

操作范围- 15.4.4 -

当创建一个范围时,浏览器会在内部创建一个文档片段节点(DocumentFragment)来包含该范围选区中的节点。文档片段是一个轻量级的容器,它不会引起页面重排(reflow),因此对文档片段的操作不会影响页面的性能。

在对范围的内容进行操作时,确保选区中的内容格式完好是很重要的。这意味着选区中的节点应该处于正常的文档结构中,并且没有断裂或不完整的情况。这样可以确保操作的正确性和预期的结果。

以下是一些与范围和文档片段操作相关的关键点:

  1. 创建范围:使用Range接口的createRange()方法创建一个范围。可以选择起始和结束节点,并使用setStart()和setEnd()方法设置范围的边界。

  2. 文档片段:在创建范围时,浏览器会隐式地创建一个文档片段节点来包含选区中的节点。可以通过调用Range对象的cloneContents()方法显式地获取该文档片段。

  3. 操作范围内容:要对范围的内容进行操作,可以使用Range对象的deleteContents()方法删除选区中的内容,或使用insertNode()方法在选区中插入节点。

  4. 保持范围完好:在进行操作之前,确保选区中的内容格式完好。这可以通过使用normalize()方法来清理相邻的文本节点或断裂的节点。

  5. 应用更改:在对范围的内容进行更改后,需要将这些更改应用到文档中。可以通过调用Document对象的insertBefore()或appendChild()方法来插入或添加新的节点,或使用removeChild()方法删除节点。

总之,通过使用文档片段和正确的操作方法,可以更轻松地在不影响性能的情况下处理和修改范围选区中的内容。

范围插入- 15.4.5 -

insertNode()方法可以在Range对象的开始位置插入一个节点。该方法接受一个参数,即要插入的节点。

在插入节点之前,需要确保Range对象是有效的,并且开始位置是在文档中的有效位置。插入节点后,Range对象的起始容器和起始偏移量会相应地更新。

以下是一个使用insertNode()方法的示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 创建一个新的元素节点  
const newNode = doc.createElement('newElement');  
newNode.textContent = 'New Text';  
  
// 使用insertNode()方法在Range的开始位置插入新节点  
const range = doc.createRange();  
range.selectNode(node);  
range.insertNode(newNode);

在这个示例中,首先遍历DOM树,找到第一个元素节点。然后,创建一个新的元素节点,并使用insertNode()方法将其插入Range的开始位置。插入节点后,Range对象的起始容器和起始偏移量会相应地更新。

范围折叠- 15.4.6 -

当Range对象没有选择文档的任何部分时,它被称为折叠(collapsed)。折叠的范围只有一个边界,即它的起始位置和结束位置是相同的。这意味着如果尝试在折叠的范围中插入节点或内容,将不会有任何效果,因为范围的起始位置和结束位置没有任何节点或内容。

折叠的范围主要用于文档编辑和处理中的特定操作,例如创建、移动或删除范围。在处理折叠的范围时,需要特别注意确保操作的正确性和有效性。

范围比较- 15.4.7 -

compareBoundaryPoints() 是 Range 接口的一个方法,它用于比较两个 Range 对象的边界点。这个方法可以用来确定两个范围之间是否存在公共的边界(起点或终点)。

该方法接受一个参数,这个参数是 RangeComparisonAlgorithm 枚举类型的一个值,用于指定比较的方法。其中,有以下几种比较算法可以选择:

  • START_TO_START:比较两个范围的起始点。

  • START_TO_END:比较两个范围的起始点与结束点。

  • END_TO_END:比较两个范围的结束点。

  • END_TO_START:比较两个范围的结束点与起始点。

compareBoundaryPoints() 方法返回一个整数,这个整数表示两个范围的边界点比较的结果。如果返回值为 0,表示两个范围的边界点相同;如果返回值为 -1 或 1,表示一个范围的边界点在另一个范围的边界点之前或之后。

这个方法在处理多个 Range 对象时非常有用,可以通过比较它们的边界点来确定这些范围之间的关系,例如确定哪些范围相交、哪些范围相邻等。

复制范围- 15.4.8 -

cloneRange() 是 Range 接口的一个方法,用于复制一个 Range 对象。这个方法返回一个新的 Range 对象,它是原始 Range 对象的深拷贝,包括起始和结束节点以及它们的子节点。

使用 cloneRange() 方法可以避免对原始 Range 对象的修改影响其他代码。通过复制 Range 对象,可以在不改变原始数据的情况下进行修改和操作。这在处理 DOM 操作和文档编辑时非常有用,因为这些操作可能会影响到文档的结构和内容。

以下是一个使用 cloneRange() 方法的示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 创建一个Range对象,选择第一个元素节点的内容  
const range = doc.createRange();  
range.selectNodeContents(node);  
  
// 调用cloneRange()方法复制Range对象  
const clonedRange = range.cloneRange();  
  
// 在复制的范围中插入一个新的文本节点  
const newText = doc.createTextNode('New Text');  
clonedRange.insertNode(newText);

在这个示例中,首先遍历DOM树,找到第一个元素节点。然后,创建一个 Range 对象,并使用 selectNodeContents() 方法选择第一个元素节点的内容。接下来,调用 cloneRange() 方法复制这个 Range 对象,并在复制的范围中插入一个新的文本节点。最后,可以根据需要使用复制的范围进行其他操作。

清理- 15.4.9 -

detach() 方法是 Range 接口的一个方法,用于从创建它的文档中剥离一个范围。当使用完一个范围后,最好调用 detach() 方法来释放与该范围相关联的资源,以避免内存泄漏。

调用 detach() 方法会将范围与文档分离,这意味着范围不再引用或关联到文档中的任何节点或内容。这样做可以确保范围不再占用任何文档资源,并且可以安全地被垃圾回收机制回收,从而释放内存空间。

在调用 detach() 方法之后,范围将不再可用或可访问。因此,确保在调用 detach() 方法之前已经完成了对范围的所有必要操作。

以下是一个使用 detach() 方法的示例:

// 创建一个DOM文档  
const xml = `  
    <root>  
        <element1>Text1</element1>  
        <element2 attribute1="value1">Text2</element2>  
        <element3 attribute2="value2">Text3</element3>  
    </root>  
`;  
  
// 解析XML文档  
const parser = new DOMParser();  
const doc = parser.parseFromString(xml, 'text/xml');  
  
// 创建TreeWalker对象  
const treeWalker = doc.createTreeWalker(  
    doc.documentElement,  
    NodeFilter.SHOW_ELEMENT,  
    null,  
    false  
);  
  
// 遍历DOM树,找到第一个元素节点  
let node;  
while (node = treeWalker.nextNode()) {  
    if (node.nodeType === Node.ELEMENT_NODE) {  
        break;  
    }  
}  
  
// 创建一个Range对象,选择第一个元素节点的内容  
const range = doc.createRange();  
range.selectNodeContents(node);  
  
// 在需要使用范围的操作完成后,调用detach()方法剥离范围  
range.detach();

在这个示例中,首先遍历DOM树,找到第一个元素节点。然后,创建一个 Range 对象,并使用 selectNodeContents() 方法选择第一个元素节点的内容。在进行完必要的操作后,调用 detach() 方法剥离该范围,确保释放与该范围相关的资源。