将 getRange 与 Shadow DOM 中的两个元素之一一起使用
Use getRange with one of the two elements inside Shadow DOM
我正在尝试获取两个给定 DOM 节点的公共偏移父节点,当两个节点都在文档内时它可以完美地工作,但当其中一个节点位于阴影 DOM.
function isOffsetContainer(element) {
return element.firstElementChild.offsetParent === element
}
function findCommonOffsetParent(element1, element2) {
const range = document.createRange();
if (element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING) {
range.setStart(element1, 0);
range.setEnd(element2, 0);
} else {
range.setStart(element2, 0);
range.setEnd(element1, 0);
}
const { commonAncestorContainer } = range;
// When one of the two elements is inside Shadow DOM, the `commonAncestorContainer`
// returned is actually one of the two given elements in the range...
// For demo purposes, we detect this situation and we `console.log` it
if ([element1, element2].includes(commonAncestorContainer)) {
console.log('Shadow DOM ');
} else {
if (isOffsetContainer(commonAncestorContainer)) {
return commonAncestorContainer;
}
const offsetParent = commonAncestorContainer && commonAncestorContainer.offsetParent;
if (!offsetParent || offsetParent && offsetParent.nodeName === 'BODY') {
return window.document.documentElement;
}
return offsetParent;
}
}
// Demo code
const reference = document.createElement('div');
reference.className = 'reference';
reference.innerText = 'reference';
const shadowParent = document.createElement('div');
document.body.appendChild(shadowParent);
document.body.appendChild(reference);
const shadow = shadowParent.createShadowRoot();
document.body.appendChild(shadow);
const popper = document.createElement('div');
popper.className = 'popper';
popper.innerText = 'popper';
shadow.appendChild(popper);
findCommonOffsetParent(popper, reference);
.popper {
width: 100px;
height: 100px;
background: red;
}
.reference {
width: 100px;
height: 100px;
background: blue;
}
如何让 createRange
与 Shadow DOM 一起工作?
您不能定义这样的范围,因为这两个元素位于不同的节点树中。
Selection is not defined. Implementation should do their best to do what's best for them. Here's one possible, admittedly naive way:
Since nodes which are in the different node trees never have the same root, there may never exist a valid DOM range that spans multiple node trees.
Accordingly, selections may only exist within one node tree, because they are defined by a single range. The selection, returned by the window.getSelection() method never returns a selection within a shadow tree.
The getSelection() method of the shadow root object returns the current selection in this shadow tree.
因此,您应该定义 2 个范围:一个在文档 DOM 树中,另一个在影子 DOM 树中。
在函数 findCommonOffsetParent()
的开头,您应该开始使用 getRootNode()
:
来测试元素是否在阴影 DOM 中
if ( element1.getRootNode().host )
//in a shadow DOM
else
//in the main document
请注意,根据您的用例,您可以嵌套 Shadow DOM 因此您可能必须递归搜索 Shadow 根...
但在一些简单的情况下(如您的示例),处理 2 个范围应该很容易。
获取共同祖先:
var shadow_host1 = element1.getRootNode().host
var shadow_host2 = element2.getRootNode().host
if (shadow_host1 ) element1 = shadow_host1
if (shadow_host2 ) element2 = shadow_host2
//create range...
我正在尝试获取两个给定 DOM 节点的公共偏移父节点,当两个节点都在文档内时它可以完美地工作,但当其中一个节点位于阴影 DOM.
function isOffsetContainer(element) {
return element.firstElementChild.offsetParent === element
}
function findCommonOffsetParent(element1, element2) {
const range = document.createRange();
if (element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING) {
range.setStart(element1, 0);
range.setEnd(element2, 0);
} else {
range.setStart(element2, 0);
range.setEnd(element1, 0);
}
const { commonAncestorContainer } = range;
// When one of the two elements is inside Shadow DOM, the `commonAncestorContainer`
// returned is actually one of the two given elements in the range...
// For demo purposes, we detect this situation and we `console.log` it
if ([element1, element2].includes(commonAncestorContainer)) {
console.log('Shadow DOM ');
} else {
if (isOffsetContainer(commonAncestorContainer)) {
return commonAncestorContainer;
}
const offsetParent = commonAncestorContainer && commonAncestorContainer.offsetParent;
if (!offsetParent || offsetParent && offsetParent.nodeName === 'BODY') {
return window.document.documentElement;
}
return offsetParent;
}
}
// Demo code
const reference = document.createElement('div');
reference.className = 'reference';
reference.innerText = 'reference';
const shadowParent = document.createElement('div');
document.body.appendChild(shadowParent);
document.body.appendChild(reference);
const shadow = shadowParent.createShadowRoot();
document.body.appendChild(shadow);
const popper = document.createElement('div');
popper.className = 'popper';
popper.innerText = 'popper';
shadow.appendChild(popper);
findCommonOffsetParent(popper, reference);
.popper {
width: 100px;
height: 100px;
background: red;
}
.reference {
width: 100px;
height: 100px;
background: blue;
}
如何让 createRange
与 Shadow DOM 一起工作?
您不能定义这样的范围,因为这两个元素位于不同的节点树中。
Selection is not defined. Implementation should do their best to do what's best for them. Here's one possible, admittedly naive way:
Since nodes which are in the different node trees never have the same root, there may never exist a valid DOM range that spans multiple node trees.
Accordingly, selections may only exist within one node tree, because they are defined by a single range. The selection, returned by the window.getSelection() method never returns a selection within a shadow tree.
The getSelection() method of the shadow root object returns the current selection in this shadow tree.
因此,您应该定义 2 个范围:一个在文档 DOM 树中,另一个在影子 DOM 树中。
在函数 findCommonOffsetParent()
的开头,您应该开始使用 getRootNode()
:
if ( element1.getRootNode().host )
//in a shadow DOM
else
//in the main document
请注意,根据您的用例,您可以嵌套 Shadow DOM 因此您可能必须递归搜索 Shadow 根...
但在一些简单的情况下(如您的示例),处理 2 个范围应该很容易。
获取共同祖先:
var shadow_host1 = element1.getRootNode().host
var shadow_host2 = element2.getRootNode().host
if (shadow_host1 ) element1 = shadow_host1
if (shadow_host2 ) element2 = shadow_host2
//create range...