如何判断元素是否在shadow DOM中?

时间:2014-12-12 23:16:55

标签: javascript html5 css-selectors shadow-dom

我有一个项目,我本地使用shadow DOM(不是通过polyfill)。我想检测给定的element是否包含在影子DOM或轻型DOM中。

我已经查看了元素的所有属性,但似乎没有根据元素所在的DOM类型而有所不同。

如何确定元素是影子DOM还是轻型DOM的一部分?


    这是一个被考虑的例子" shadow DOM"和"轻DOM"为了这个问题的目的。

 (light root) • Document
      (light)   • HTML
      (light)   | • BODY
      (light)   |   • DIV
(shadow root)   |     • ShadowRoot
     (shadow)   |       • DIV 
     (shadow)   |         • IFRAME 
 (light root)   |           • Document
      (light)   |             • HTML
      (light)   |             | • BODY
      (light)   |             |   • DIV
(shadow root)   |             |     • ShadowRoot
     (shadow)   |             |       • DIV
       (none)   |             • [Unattached DIV of second Document]
       (none)   • [Unattached DIV of first Document]



<!doctype html>
<title>
  isInShadow() test document - can not run in Stack Exchange's sandbox
</title>
<iframe src="about:blank"></iframe>
<script>

function isInShadow(element) {
  // TODO
}

function test() {
  //  (light root) • Document
  //       (light)   • HTML
  var html = document.documentElement;

  console.assert(isInShadow(html) === false);

  //       (light)   | • BODY
  var body = document.body;

  console.assert(isInShadow(body) === false);

  //       (light)   |   • DIV
  var div = document.createElement('div');
  body.appendChild(div);

  console.assert(isInShadow(div) === false);

  // (shadow root)   |     • ShadowRoot
  var divShadow = div.createShadowRoot();

  var shadowDiv = document.createElement('div');
  divShadow.appendChild(shadowDiv);

  //      (shadow)   |       • DIV 
  console.assert(isInShadow(shadowDiv) === true);

  //      (shadow)   |         • IFRAME 
  var iframe = document.querySelector('iframe');
  shadowDiv.appendChild(iframe);

  console.assert(isInShadow(iframe) === true);

  //  (light root)   |           • Document
  var iframeDocument = iframe.contentWindow.document;

  //       (light)   |             • HTML
  var iframeHtml = iframeDocument.documentElement;

  console.assert(isInShadow(iframeHtml) === false);

  //       (light)   |             | • BODY
  var iframeBody = iframeDocument.body;

  //
  console.assert(isInShadow(iframeHtml) === false);

  //       (light)   |             |   • DIV
  var iframeDiv = iframeDocument.createElement('div');
  iframeBody.appendChild(iframeDiv);
   
  console.assert(isInShadow(iframeDiv) === false);
   
  // (shadow root)   |             |     • ShadowRoot
  var iframeDivShadow = iframeDiv.createShadowRoot();

  //      (shadow)   |             |       • DIV
  var iframeDivShadowDiv = iframeDocument.createElement('div');
  iframeDivShadow.appendChild(iframeDivShadowDiv);
    
  console.assert(isInShadow(iframeDivShadowDiv) === true);
     
  //        (none)   |             • [Unattached DIV of second Document]
  var iframeUnattached = iframeDocument.createElement('div');
    
  console.assert(Boolean(isInShadow(iframeUnattached)) === false);

  //        (none)   • [Unattached DIV of first Document]
  var rootUnattached = document.createElement('div');
    
  console.assert(Boolean(isInShadow(rootUnattached)) === false);
}

onload = function main() {
  console.group('Testing');
  try {
    test();
    console.log('Testing complete.');
  } finally {
    console.groupEnd();
  }
}

</script>
&#13;
&#13;
&#13;

5 个答案:

答案 0 :(得分:29)

如果您调用ShadowRoot的toString()方法,它将返回"[object ShadowRoot]"。根据这个事实,这是我的方法:

function isInShadow(node) {
    var parent = (node && node.parentNode);
    while(parent) {
        if(parent.toString() === "[object ShadowRoot]") {
            return true;
        }
        parent = parent.parentNode;
    }
    return false;
}

修改

Jeremy Banks建议采用另一种循环方式。这种方法与我的有点不同:它还检查传递的节点本身,我没有这样做。

function isInShadow(node) {
    for (; node; node = node.parentNode) {
        if (node.toString() === "[object ShadowRoot]") {
            return true;
        }
    }
    return false;
}

function isInShadow(node) {
    for (; node; node = node.parentNode) {
        if (node.toString() === "[object ShadowRoot]") {
            return true;
        }
    }
    return false;
}

console.group('Testing');

var lightElement = document.querySelector('div');    

console.assert(isInShadow(lightElement) === false);

var shadowChild = document.createElement('div');
lightElement.createShadowRoot().appendChild(shadowChild);

console.assert(isInShadow(shadowChild) === true);

var orphanedElement = document.createElement('div');

console.assert(isInShadow(orphanedElement) === false);

var orphanedShadowChild = document.createElement('div');
orphanedElement.createShadowRoot().appendChild(orphanedShadowChild);

console.assert(isInShadow(orphanedShadowChild) === true);

var fragmentChild = document.createElement('div');
document.createDocumentFragment().appendChild(fragmentChild);

console.assert(isInShadow(fragmentChild) === false);

console.log('Complete.');
console.groupEnd();
<div></div>

答案 1 :(得分:8)

  

⚠️警告:弃用风险

     

::shadow伪元素is deprecated in and being removed from from the dynamic selector profile。下面的方法只要求它保留在静态选择器配置文件中,但可能也将被弃用并在将来删除。 Discussions are ongoing

我们可以使用Element's .matches() method来确定元素是否附加到影子DOM。

当且仅当元素在阴影DOM中时,我们才能通过使用选择器:host来匹配它,以识别具有阴影DOM的元素,::shadow来查看这些阴影DOM和*以及匹配任何后代。

function isInShadow(element) {
  return element.matches(':host::shadow *');
}

function isInShadow(element) {
  return element.matches(':host::shadow *');
}

console.group('Testing');

var lightElement = document.querySelector('div');    

console.assert(isInShadow(lightElement) === false);

var shadowChild = document.createElement('div');
lightElement.createShadowRoot().appendChild(shadowChild);

console.assert(isInShadow(shadowChild) === true);

var orphanedElement = document.createElement('div');

console.assert(isInShadow(orphanedElement) === false);

var orphanedShadowChild = document.createElement('div');
orphanedElement.createShadowRoot().appendChild(orphanedShadowChild);

console.assert(isInShadow(orphanedShadowChild) === true);

var fragmentChild = document.createElement('div');
document.createDocumentFragment().appendChild(fragmentChild);

console.assert(isInShadow(fragmentChild) === false);

console.log('Complete.');
console.groupEnd();
<div></div>

答案 2 :(得分:6)

您可以检查元素是否具有如下阴影父级:

function hasShadowParent(element) {
    while(element.parentNode && (element = element.parentNode)){
        if(element instanceof ShadowRoot){
            return true;
        }
    }
    return false;
}

这比instanceof使用.toString()

答案 3 :(得分:5)

让我们了解Light Dom:

Light DOM是用户提供的托管阴影根的元素的DOM。 欲了解更多信息,请阅读聚合物项目。

https://www.polymer-project.org/platform/shadow-dom.html#shadow-dom-subtrees

这意味着:Light DOM 总是 相对 托管阴影根的下一个祖先。< / p>

元素可以是自定义元素的光源的一部分,而它可以是另一个自定义元素同时的影子根的一部分。

示例:

<my-custom-element>
    <shadowRoot>

        <custom-element>
            <div>I'm in Light DOM of "custom-element" and 
                    in Shadow Root of "my-custom-element" at same time</div>
        </custom-element>

    </shadowRoot>

    <div id="LDofMCE"> Im in Light DOM of "my-custom-element"</div>

<my-custom-element>

根据你的问题:

如果您想知道元素是否在阴影根中,您只需要从文档中获取该元素。

var isInLD = document.contains(NodeRef);
if(isInLD){
    console.alert('Element is in the only available "global Light DOM"(document)');
} else {
    console.log('Element is hidden in the shadow dom of some element');
}

唯一没有影子Root的Light DOM是文档的一部分,因为Light DOM是相对的,如上所示。

它不向后工作:如果它的部分文档根本不在Light DOM中。您需要检查其中一个祖先是否正在托管阴影根,如 Leo 中建议的那样。

您可以将此方法与其他元素一起使用。只需更换&#34;文件&#34;与... &#34;我的定制元素&#34;并测试{DOM}相对于&#34; my-custom-element&#34;是否div#LDofMCE

由于缺乏有关您需要此信息的原因的信息,我无法接近......

修改

它不能倒退应该理解如下:

这个元素是阴影根吗?:document.contains()或来自Leo的isInShadow(节点)方法提供答案。

&#34;向后&#34;问题:这个元素是否在Light DOM中(如果你开始相对于文档搜索)?:domcument.contains()没有提供答案,因为它位于Light Dom中 - 其中一个元素祖先需要成为影子主人。

来点

  • Light DOM是相对的。
  • 元素可以同时参与阴影根和光照。没有&#34; 是影子DOM OR 轻型DOM的一部分?&#34;

答案 4 :(得分:0)

可以使用getRootNode来找到shadowDOM中的元素,如下所示。

function isInShadow(node) {
   return node.getRootNode() instanceof ShadowRoot;
}