Selection:getComposedRanges() 方法
Selection.getComposedRanges() 方法返回一个 StaticRange 对象数组,表示当前选择的范围,并且可以返回可能跨越 Shadow DOM 边界的范围。
由于选择范围的端点可能位于 Shadow DOM 树内,甚至可能位于不同的 Shadow DOM 树内,并且这些 Shadow DOM 树可能是闭合的,因此该方法默认无法返回 Shadow DOM 树内的节点。如果该方法需要返回包含 Shadow DOM 树内节点的选区,则必须将这些树的 ShadowRoot 对象作为参数传递给该方法。如果未提供相应的 ShadowRoot,并且选区的开始或结束点位于 Shadow DOM 树内,则返回的范围将被重新限定,以包含 ShadowRoot 的宿主元素,而不是 ShadowRoot 内的某个节点。
返回的范围表示调用 getComposedRanges() 时的时间范围。如果 DOM 或 Shadow DOM 被修改,选定的范围很可能不正确。应用程序代码可以使用 MutationObserver 来监视 DOM 修改,然后调用 Selection.setBaseAndExtent() 来更新选区。
注意: 在选择可能跨越 Shadow DOM 根边界的范围时,应使用此方法代替 Selection.getRangeAt()。 Selection.getRangeAt() 不了解 Shadow DOM 根。返回的范围未指定,并且在不同浏览器中有所不同。
语法
getComposedRanges()
getComposedRanges(options)
参数
options可选-
一个具有以下属性的对象,所有属性都是可选的
shadowRoots可选-
一个
ShadowRoot对象数组。如果选择的端点位于提供的 Shadow DOM 根之一内,则范围将能够返回其对应的 Shadow DOM 树内的节点。否则,如果选区跨越 Shadow DOM 边界但未提供相应的ShadowRoot,则返回的范围将被调整为包含 Shadow DOM 根的整个宿主元素。
注意: 在原始规范中,Shadow DOM 根被指定为一组 rest 参数。某些浏览器可能仍支持此旧语法。
返回值
一个 StaticRange 对象数组,表示文档组合(已展平)树中选定的范围。撰写本文时,规范期望此数组仅包含一个对象。
示例
跨内联 Shadow DOM 根进行选择
此示例演示了 getComposedRanges() 在传递 Shadow DOM 根和不传递 Shadow DOM 根时的行为,并与 Selection.getRangeAt() 进行对比。
它允许您选择定义在不同 DOM 节点中以及开放和闭合 Shadow DOM 根中的文本,使用不同的方法复制选区范围,然后重新应用该范围以查看原始选区效果如何。
HTML
HTML 定义了一些文本节点和一些 <span> 元素,我们将使用 JavaScript 向其附加 Shadow DOM 根。我们还添加了一些按钮,用于使用多种不同方法复制和应用选区。
<p>
DOM Text One<span id="openHost"></span>DOM Text Two<span
id="closedHost"></span
>DOM Text Three
</p>
<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 没有做任何有趣的事情。我们只是将按钮垂直排列,以便更容易阅读。
button {
display: block;
}
JavaScript
大部分工作都发生在 JavaScript 中。首先,我们记录 getComposedRanges() 是否不受支持,尽管我们并没有阻止示例的其余部分尝试使用它。
if (!("getComposedRanges" in Selection.prototype)) {
log("getComposedRanges() method not supported in this browser");
}
然后,我们创建了一个开放和一个闭合的 Shadow DOM 根,并将它们附加到我们在 HTML 中创建的两个 <span> 元素上。这些元素包含一些简单的粗体文本,以便在渲染 HTML 时可以轻松识别 Shadow DOM 节点。
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() 获取选定的范围。第一个按钮在不传递 Shadow DOM 根的情况下调用 getComposedRanges(),第二个按钮则传递了两个 Shadow DOM 根。在这两种情况下,组合范围都会保存到一个变量中。
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({ shadowRoots: [openRoot, closedRoot] })[0];
log(`Selection has been copied (shadow roots passed)`);
});
下面显示了“应用选区”按钮的处理程序。该处理程序调用 setBaseAndExtent() 来设置当前选区,传递来自保存范围的节点和偏移量。
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() 复制当前选区范围,然后重新应用选区的按钮。
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 到 Shadow DOM 根的节点。如果您选择“复制范围(传递 Shadow DOM 根)”按钮,然后单击“应用选区”按钮,您会注意到选区没有改变,因为代码可以访问 Shadow DOM 根中的所有节点,即使它是闭合的。如果您接着选择“复制范围(不传递 Shadow DOM 根)”按钮,然后单击“应用”,则选区将扩展到 Shadow DOM 根中所有文本的末尾。这是因为选区被重新限定到了宿主节点的末尾,因为 getComposedRanges() 方法没有获得 Shadow DOM 树内部的可见性。
还可以测试使用“使用 getRangeAt() 复制范围”和“应用选区”按钮会发生什么。您应该会发现,如果您跨入 Shadow DOM 根,选定范围会相当随意,并且取决于您使用的浏览器。
规范
| 规范 |
|---|
| Selection API # dom-selection-getcomposedranges |
浏览器兼容性
加载中…
另见
Selection,它所属的接口。