将getRange与Shadow DOM中的两个元素之一一起使用

时间:2017-04-12 12:44:45

标签: javascript dom shadow-dom

我试图获得两个给定DOM节点的公共偏移父节点,当两个节点都在文档内部时它可以完美地工作,但是当它们中的一个在Shadow DOM中时它不会。 / p>



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合作?

1 个答案:

答案 0 :(得分:1)

您无法定义这样的范围,因为这两个元素位于不同的节点树中。

根据the specification

  

未定义选择。实施应尽力为他们做最好的事情。这是一种可能的,公认的天真的方式:

     

由于不同节点树中的节点永远不会具有相同的根,因此可能永远不会存在跨越多个节点树的有效DOM范围。

     

因此,选择可能仅存在于一个节点树内,因为它们由单个范围定义。 window.getSelection()方法返回的选择永远不会返回阴影树中的选择。

     

阴影根对象的getSelection()方法返回此阴影树中的当前选择。

因此,您应该定义2个范围:一个在文档DOM树中,另一个在shadow DOM树中。

在函数findCommonOffsetParent()的开头,您应该使用getRootNode()开始测试元素是否在shadow 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...