选择:getComposedRanges() 方法

实验性: 这是一项 实验性技术
在生产环境中使用此方法之前,请仔细查看 浏览器兼容性表

Selection.getComposedRanges() 方法返回一个 StaticRange 对象数组,表示当前选择范围,并且可以返回可能跨越阴影边界范围的范围。

由于选择范围的端点可能位于阴影树内,甚至位于不同的阴影树内,并且由于这些树可能是封闭的,因此该方法默认情况下无法从阴影树内返回节点。如果该方法需要返回一个包含阴影树内节点的选择,那么这些树的 ShadowRoot 对象必须作为参数传递给该方法。如果未提供相应的根节点,并且选择的起点或终点位于阴影树内,则返回的范围将重新调整范围,以包含阴影根的宿主,而不是根内的某个节点。

返回的范围表示调用 getComposedRanges() 时范围的状态。如果 DOM 或阴影 DOM 发生了变动,则选择范围很可能不正确。应用程序代码可以使用 MutationObserver 来监视 DOM 变动,然后调用 Selection.setBaseAndExtent() 来更新选择。

注意: 当选择可能跨越阴影根边界的范围时,应使用此方法代替 Selection.getRangeAt()Selection.getRangeAt() 不了解阴影根。返回范围未指定,并且在不同浏览器之间有所不同。

语法

js
getComposedRanges()
getComposedRanges(shadowRoot1)
getComposedRanges(shadowRoot1, shadowRoot2)
getComposedRanges(shadowRoot1, shadowRoot2, /* …, */ shadowRootN)

参数

shadowRoot1、…、shadowRootN

零个或多个 ShadowRoot 参数。如果选择端点位于提供的阴影根之一内,则该范围将能够返回其对应阴影 DOM 树内的节点。否则,如果选择跨越阴影边界,并且未提供相应的 ShadowRoot,则返回的范围将调整为包含阴影根的整个宿主元素。

返回值

一个 StaticRange 对象数组,表示文档的组合(扁平化)树中的选择范围。在编写规范时,该数组预计只包含一个对象。

示例

跨行内阴影根选择

此示例演示了 getComposedRanges() 的行为,包括传递和未传递阴影根的情况,并与 Selection.getRangeAt() 相比。

它允许你选择 DOM 中不同节点(包括开放和封闭阴影根)中定义的文本,使用不同的方法复制选择范围,然后重新应用范围,以查看原始选择的有效性。

HTML

HTML 定义了一些文本节点,其中包含一些 <span> 元素,我们将使用 JavaScript 将阴影根附加到这些元素。我们还添加了一些按钮,用于使用多种不同方法复制和应用选择。

html
<p>
  DOM Text One<span id="openHost"></span>DOM Text Two<span
    id="closedHost"></span
  >DOM Text Three
</p>
html
<button id="copySelection">Copy range not passing shadow roots</button>
<button id="copySelectionWithShadowRoots">
  Copy range passing shadow roots
</button>
<button id="applySelection">Apply selection</button>
<hr />
<button id="copySelectionRangeAt">Copy range with getRangeAt()</button>
<button id="applySelectionGetRangeAt">Apply selection</button>

CSS

CSS 没有做任何有趣的事情。我们只是垂直排列按钮,使它们更容易阅读。

css
button {
  display: block;
}

JavaScript

大部分工作都在 JavaScript 中完成。首先,我们记录 getComposedRanges() 是否不受支持,但我们实际上并没有阻止示例的其他部分尝试使用它。

js
if (!("getComposedRanges" in Selection.prototype)) {
  log("getComposedRanges() method not supported in this browser");
}

然后,我们创建了一个开放阴影根和一个封闭阴影根,并将它们附加到我们在 HTML 中创建的两个 <span> 元素。这些元素包含一些简单的加粗文本,以便我们可以在 HTML 渲染时轻松识别阴影节点。

js
let openRoot = openHost.attachShadow({ mode: "open" });
openRoot.innerHTML = `<b>Open Shadow DOM Text</b>`;

let closedRoot = closedHost.attachShadow({ mode: "closed" });
closedRoot.innerHTML = `<b>Closed Shadow DOM Text</b>`;

接下来,我们创建代码,以便在单击前两个按钮时使用 getComposedRanges() 获取选择范围。第一个按钮在不传递阴影根的情况下调用 getComposedRanges(),而第二个按钮传递了两个阴影根。在这两种情况下,组合范围都保存到一个变量中。

js
const copySelectionButton = document.querySelector("#copySelection");
let composedRangeSelection = null;
copySelectionButton.addEventListener("click", () => {
  composedRangeSelection = window.getSelection().getComposedRanges()[0];
  log(`Selection copied (no shadow roots passed)`);
});

const copySelectionWithShadowRootsButton = document.querySelector(
  "#copySelectionWithShadowRoots",
);
copySelectionWithShadowRootsButton.addEventListener("click", () => {
  composedRangeSelection = window
    .getSelection()
    .getComposedRanges(openRoot, closedRoot)[0];
  log(`Selection has been copied (shadow roots passed)`);
});

"应用选择"按钮的处理程序如下所示。它调用 setBaseAndExtent() 来设置当前选择,并传递保存范围中的节点和偏移量。

js
const applySelectionButton = document.querySelector("#applySelection");
applySelectionButton.addEventListener("click", () => {
  if (composedRangeSelection) {
    window
      .getSelection()
      .setBaseAndExtent(
        composedRangeSelection.startContainer,
        composedRangeSelection.startOffset,
        composedRangeSelection.endContainer,
        composedRangeSelection.endOffset,
      );
    log(`Selection applied`);
  } else {
    log(`No selection to apply`);
  }
});

代码的最后部分定义了一些按钮,用于使用 Selection.getRangeAt() 复制当前选择范围,然后重新应用选择。

js
const copySelectionRangeAtButton = document.querySelector(
  "#copySelectionRangeAt",
);
let rangeSelection = null;
copySelectionRangeAtButton.addEventListener("click", () => {
  const selection = window.getSelection();
  if (selection.rangeCount > 0) {
    log(`Selection copied using getRangeAt()`);
    rangeSelection = selection.getRangeAt(0);
  } else {
    log(`No range selected`);
  }
});

const applySelectionGetRangeAtButton = document.querySelector(
  "#applySelectionGetRangeAt",
);
applySelectionGetRangeAtButton.addEventListener("click", () => {
  if (rangeSelection) {
    window
      .getSelection()
      .setBaseAndExtent(
        rangeSelection.startContainer,
        rangeSelection.startOffset,
        rangeSelection.endContainer,
        rangeSelection.endOffset,
      );
    log(`Selection applied`);
  } else {
    log(`No selection to apply`);
  }
});

结果

正在运行的示例如下所示。从一些普通文本开始,到加粗部分结束,选择顶行中的文本,这样你就能从 DOM 中选择节点到阴影根中。如果你选择 "复制传递阴影根的范围",然后选择 "应用选择" 按钮,你会注意到选择没有改变,因为代码已经提供了对阴影根中所有节点的访问权限,即使它是封闭的。如果你然后选择 "复制不传递阴影根的范围" 按钮,然后应用,选择将扩展到阴影根中的文本末尾。这是因为选择范围被重新调整到宿主节点的末尾,因为 getComposedRanges() 方法没有获得对阴影树内部的可见性。

还可以测试使用 "使用 getRangeAt() 复制范围" 和 "应用选择" 按钮会发生什么。你应该发现,如果跨入阴影根,则选择的范围非常随意,并且在不同的浏览器中有所不同。

规范

规范
选择 API
# dom-selection-getcomposedranges

浏览器兼容性

BCD 表只在浏览器中加载

另请参阅