JavaScript 中的 Range 和 Selection 物件

日升_rs發表於2024-05-28

JavaScript 中的 Range 和 Selection 物件

前言

最近在做滑鼠框選的需求,滑鼠框選就需要用到 RangeSelection 物件。

Range 表示選擇的區間範圍,Selection 表示選擇的文件內容。

下面就詳細說下這兩個物件

一、Range

Range 介面表示一個包含節點與文字節點的一部分的文件片段。

不僅僅可以用於滑鼠框選,頁面上任何元素、文字都可以建立 Range

Range 是瀏覽器原生的物件。

image

1. 建立 Range 例項,並設定起始位置

<body>
  <ul>
    <li>Vite</li>
    <li>Vue</li>
    <li>React</li>
    <li>VitePress</li>
    <li>NaiveUI</li>
  </ul>
</body>
<script>
  // 建立 Range 物件
  const range = new Range()
  const liDoms = document.querySelectorAll("li");
  // Range 起始位置在 li 2
  range.setStartBefore(liDoms[1]);
  // Range 結束位置在 li 3
  range.setEndAfter(liDoms[2]);
  // 獲取 selection 物件
  const selection = window.getSelection();
  // 新增游標選擇的範圍
  selection.addRange(range);
</script>

image

可以看到,選擇內容為第二行和第三行

1.1 瀏覽器相容情況

image

2. Range 屬性

  1. startContainer:起始節點。

  2. startOffset:起始節點偏移量。

  3. endContainer:結束節點。

  4. endOffset:結束節點偏移量。

  5. collapsed:範圍的開始和結束是否為同一點。

  6. commonAncestorContainer:返回完整包含 startContainerendContainer 的最深一級的節點。

2.1. 用我們上面建立的例項來看下 range 屬性的值

image

2.2. 如果我們只選擇文字內容時

只選擇 li 中的 itePres

image

可以看出 range 屬性對應的值

image

3. Range 方法

3.1. cloneContents()

cloneContents():複製範圍內容,並將複製的內容作為 DocumentFragment 返回。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const documentFragment = range.cloneContents();
document.body.appendChild(documentFragment);

3.2. cloneRange()

cloneRange():建立一個具有相同起點/終點的新範圍, 非引用,可以隨意改變,不會影響另一方。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const clone = range.cloneRange();

3.3. collapse()

collapse(toStart):如果 toStart=true 則設定 end=start,否則設定 start=end,從而摺疊範圍。

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.selectNode(referenceNode);
range.collapse(true);

3.4. compareBoundaryPoints()

compareBoundaryPoints(how, sourceRange):兩個範圍邊界點進行比較,返回一個數字 -1、0、1。

const range = document.createRange();
range.selectNode(document.querySelector("div"));
const sourceRange = document.createRange();
sourceRange.selectNode(document.getElementsByTagName("div")[1]);
const compare = range.compareBoundaryPoints(Range.START_TO_END, sourceRange);

3.5. comparePoint()

comparePoint(referenceNode, offset):返回-1、0、1具體取決於 是 referenceNode 在 之前、相同還是之後。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const returnValue = range.comparePoint(document.getElementsByTagName("p").item(0), 1);

3.6. createContextualFragment()

createContextualFragment(tagString):返回一個 DocumentFragment

const tagString = "<div>I am a div node</div>";
const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const documentFragment = range.createContextualFragment(tagString);
document.body.appendChild(documentFragment);

3.7. deleteContents()

deleteContents():刪除框選的內容。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
range.deleteContents();

3.8. extractContents()

extractContents():從文件中刪除範圍內容,並將刪除的內容作為 DocumentFragment 返回。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const documentFragment = range.extractContents();
document.body.appendChild(documentFragment);

3.9. getBoundingClientRect()

getBoundingClientRect():和 dom 一樣,返回 DOMRect 物件。

const range = document.createRange();
range.setStartBefore(document.getElementsByTagName("em").item(0));
range.setEndAfter(document.getElementsByTagName("em").item(1));
const clientRect = range.getBoundingClientRect();
const highlight = document.getElementById("highlight");
highlight.style.left = `${clientRect.x}px`;
highlight.style.top = `${clientRect.y}px`;
highlight.style.width = `${clientRect.width}px`;
highlight.style.height = `${clientRect.height}px`;

3.10. getClientRects()

getClientRects():返回可迭代的物件序列 DOMRect

const range = document.createRange();
range.selectNode(document.querySelector("div"));
const rectList = range.getClientRects();
const output = document.querySelector("#output");
for (const rect of rectList) {
  output.textContent = `${output.textContent}\n${rect.width}:${rect.height}`;
}

3.11. insertNode()

insertNode(node):在範圍的起始處將 node 插入文件。

const range = document.createRange();
const newNode = document.createElement("p");
newNode.appendChild(document.createTextNode("New Node Inserted Here"));
range.selectNode(document.getElementsByTagName("div").item(0));
range.insertNode(newNode);

3.12. intersectsNode()

intersectsNode(referenceNode):判斷與給定的 node 是否相交。

const range = document.createRange();
range.selectNode(document.getElementsByTagName("div").item(0));
const intersectingNode = range.intersectsNode(
  document.getElementsByTagName("p").item(0),
);

3.13. selectNode()

selectNode(node):設定範圍以選擇整個 node

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.selectNode(referenceNode);

3.14. selectNodeContents()

selectNodeContents(node):設定範圍以選擇整個 node 的內容。

const range = document.createRange();
const referenceNode = document.querySelector("div");
range.selectNodeContents(referenceNode);

3.15. setStart()

setStart(startNode, startOffset):設定起點。

const element = document.getElementById("content");
const textNode = element.childNodes[0];
const range = document.createRange();
range.setStart(textNode, 0); // Start at first character
range.setEnd(textNode, 5); // End at fifth character
document.getElementById("log").textContent = range;

3.16. setEnd()

setEnd(endNode, endOffset):設定終點。

const range = document.createRange();
const endNode = document.getElementsByTagName("p").item(3);
const endOffset = endNode.childNodes.length;
range.setEnd(endNode, endOffset);

3.17. setStartBefore()

setStartBefore(node):將起點設定在 node 前面。

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.setStartBefore(referenceNode);

3.19. setStartAfter()

setStartAfter(node):將起點設定在 node 後面。

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.setStartAfter(referenceNode);

3.19. setEndBefore()

setEndBefore(node):將終點設定為 node 前面。

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.setEndBefore(referenceNode);

3.20. setEndAfter()

setEndAfter(node):將終點設定為 node 後面。

const range = document.createRange();
const referenceNode = document.getElementsByTagName("div").item(0);
range.setEndAfter(referenceNode);

3.21. surroundContents()

surroundContents(node):使用 node 將所選範圍內容包裹起來。

const range = document.createRange();
const newParent = document.createElement("h1");
range.selectNode(document.querySelector(".header-text"));
range.surroundContents(newParent);

4. 建立 Range 的方法

4.1. Document.createRange

const range = document.createRange();

4.2. Selection 的 getRangeAt() 方法

const range = window.getSelection().getRangeAt(0)

4.3. caretRangeFromPoint() 方法

if (document.caretRangeFromPoint) {
    range = document.caretRangeFromPoint(e.clientX, e.clientY);
}

4.4. Range() 建構函式

const range = new Range()

5. Range 相容性

image

  • 【詳細相容性:Can I use】

二、Selection

Selection 物件表示使用者選擇的文字範圍或插入符號的當前位置。它代表頁面中的文字選區,可能橫跨多個元素。

1. 獲取文字物件

window.getSelection()

image

image

2. Selection 術語

2.1. 錨點 (anchor)

錨指的是一個選區的起始點(不同於 HTML 中的錨點連結)。當我們使用滑鼠框選一個區域的時候,錨點就是我們滑鼠按下瞬間的那個點。在使用者拖動滑鼠時,錨點是不會變的。

2.2. 焦點 (focus)

選區的焦點是該選區的終點,當你用滑鼠框選一個選區的時候,焦點是你的滑鼠鬆開瞬間所記錄的那個點。隨著使用者拖動滑鼠,焦點的位置會隨著改變。

2.3. 範圍 (range)

範圍指的是文件中連續的一部分。一個範圍包括整個節點,也可以包含節點的一部分,例如文字節點的一部分。使用者通常下只能選擇一個範圍,但是有的時候使用者也有可能選擇多個範圍。

2.4. 可編輯元素 (editing host)

一個使用者可編輯的元素(例如一個使用 contenteditableHTML 元素,或是在啟用了 designModeDocument 的子元素)。

3. Selection 的屬性

首先要清楚,選擇的起點稱為錨點(anchor),終點稱為焦點(focus)。

  1. anchorNode:選擇的起始節點。

  2. anchorOffset:選擇開始的 anchorNode 中的偏移量。

  3. focusNode:選擇的結束節點。

  4. focusOffset:選擇開始處 focusNode 的偏移量。

  5. isCollapsed:如果未選擇任何內容(空範圍)或不存在,則為 true

  6. rangeCount:選擇中的範圍數,之前說過,除 Firefox 外,其他瀏覽器最多為1。

  7. type:型別:NoneCaretRange

image

image

4. Selection 方法

4.1. addRange()

addRange(range): 將一個 Range 物件新增到當前選區。

const strongs = document.getElementsByTagName("li");
const selection = window.getSelection();
for (var i = 0; i < strongs.length; i++) {
  const range = document.createRange();
  range.selectNode(strongs[i]);
  selection.addRange(range);
}

4.2. collapse()

collapse(node, offset): 將選區摺疊到指定的節點和偏移位置。

const body = document.getElementsByTagName("body")[0];
window.getSelection().collapse(body, 0);

4.3. collapseToEnd()

collapseToEnd(): 將選區摺疊到當前選區的末尾,並把游標定位在原選區的末尾處。

const selection = window.getSelection();
selection.collapseToEnd()

4.4. collapseToStart()

collapseToStart(): 將選區摺疊到當前選區的起始位置,並把游標定位在原選區的最開始處。

const selection = window.getSelection();
selection.collapseToStart()

4.5. containsNode()

containsNode(node, partlyContained): 判斷選區是否包含指定的節點,可以選擇是否部分包含。

window.getSelection().containsNode(document.body, true);

4.6. deleteFromDocument()

deleteFromDocument(): 從文件中刪除選區內容。

const selection = window.getSelection();
selection.deleteFromDocument();

4.7. empty()

empty(): 從選區中移除所有範圍(同 `removeAllRanges()``,已廢棄)。

const selection = window.getSelection();
selection.empty();

4.8. extend()

extend(node, offset): 將選區的焦點節點擴充套件到指定的節點和偏移位置。

const selection = window.getSelection();
selection.extend(document.body, 3)

4.9. getRangeAt()

getRangeAt(index): 返回選區中指定索引處的 Range 物件。

const  ranges = [];
const selection = window.getSelection();
for (let i = 0; i < selection.rangeCount; i++) {
  ranges[i] = selection.getRangeAt(i);
}

4.10. removeAllRanges()

removeAllRanges(): 移除所有選區中的範圍。

const selection = window.getSelection();
selection.removeAllRanges();

4.11. removeRange()

removeRange(range): 從選區中移除指定的 Range 物件。

const selection = window.getSelection();
if (selection.rangeCount > 1) {
  for (let i = 1; i < selection.rangeCount; i++) {
    selection.removeRange(selection.getRangeAt(i));
  }
}

4.12. selectAllChildren()

selectAllChildren(node): 選中指定節點的所有子節點。

window.getSelection().selectAllChildren(document.body);

4.13. setBaseAndExtent()

setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset): 設定選區的起始和結束節點及偏移位置。

window.getSelection().setBaseAndExtent(document.getElementById('li1'), 0, document.getElementById('li3'), 1)

4.14. setPosition()

setPosition(node, offset)collapse 的別名

window.getSelection().setPosition(document.body, 0);

5. Selection 相容性

image

  • 【詳細相容性:Can I use】

到此結束...

相關文章