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

时间:2022-09-28 07:17:18

I have a project where I'm using the shadow DOM natively (not through a polyfill). I'd like to detect if a given element is contained within a shadow DOM or a light DOM.

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

I've looked through all of the properties on the elements, but there don't seem to be any which vary based on the type of DOM an element is in.

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

How can I determine if an element is part of a shadow DOM or a light DOM?

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


Here is an example of what is considered "shadow DOM" and "light DOM" for the purpose of this question.

以下是针对此问题的“shadow DOM”和“light 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>

4 个解决方案

#1


29  

If you call a ShadowRoot's toString() method, it will return "[object ShadowRoot]". According to this fact, here's my approach:

如果调用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;
}

EDIT

Jeremy Banks suggests an approach in another style of looping. This approach is a little different from mine: it also checks the passed node itself, which I didn't do.

杰里米·班克斯(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>

#2


8  

⚠️ Warning: Deprecation Risk

The ::shadow pseudo-element is deprecated in and being removed from from the dynamic selector profile. The approach below only requires that it remain in the static selector profile, but it may also be deprecated and removed there in the future. Discussions are ongoing.

不推荐使用:: shadow伪元素,并将其从动态选择器配置文件中删除。下面的方法只要求它保留在静态选择器配置文件中,但它也可能在将来被弃用并删除。讨论正在进行中。

We can use Element's .matches() method to determine if an element is attached to a shadow DOM.

我们可以使用Element的.matches()方法来确定元素是否附加到shadow DOM。

If and only if the element is in a shadow DOM, we will be able to match it by using the selector :host to identify elements that have a Shadow DOM, ::shadow to look in those shadow DOMs, and * and to match any descendant.

当且仅当元素在shadow DOM中时,我们才能通过使用selector:host来匹配它,以识别具有Shadow DOM,:: shadow以查看那些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>

#3


6  

You can check if an element has a shadow parent like this:

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

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

This uses instanceof over .toString().

这使用了ofofof .toString()。

#4


5  

Lets understand Light Dom:

让我们了解Light Dom:

The Light DOM is the user supplied DOM of an element that hosts a shadow root. For more info read at polymer-project.

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

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

This means: Light DOM is always relative to the next ancestor which hosts a shadow root.

这意味着:Light DOM始终相对于托管阴影根的下一个祖先。

An Element can be a part of the light dom of a custom element while it can be a part of the shadow root of another custom element at same time.

Element可以是自定义元素的light dom的一部分,同时它可以是另一个自定义元素的shadow root的一部分。

Example:

<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>

According to your question:

根据你的问题:

If you want to know if an element is in a shadow root, you just need to grab the element out of the document.

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

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');
}

The only Light DOM which is not preceeded by a shadow Root is part of the document, because Light DOM is relative as shown above.

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

It doesnt work backwards: if its part of the document its not in a Light DOM at all. You need to check if one of the ancestors is hosting a Shadow Root like suggested from Leo.

它不会倒退:如果它的部分文档根本不在Light DOM中。您需要检查其中一个祖先是否正在托管像Leo建议的影子根。

You can use this approach with other elements to. Just replace the "document" with e.g. "my-custom-element" and test if div#LDofMCE is in Light DOM relative to "my-custom-element".

您可以将此方法与其他元素一起使用。只需将“文档”替换为例如“my-custom-element”并测试div#LDofMCE是否在Light DOM中相对于“my-custom-element”。

Because of the lack of information about why you need this information i cant get closer...

由于缺乏有关您为什么需要这些信息的信息,我无法接近......

EDIT:

It doesnt work backwards should be understand as follows:

它不倒退应该理解如下:

Is this element in a Shadow Root?: document.contains() or the isInShadow(node) method from Leo deliver the answer.

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

"backwards" Question: Is this element in a Light DOM (In case you start searching relative to document)?: domcument.contains() does not deliver the answer because to be in a Light Dom - one of the elements ancestors needs to be a shadow host.

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

Come to the Point

来点

  • Light DOM is relative.
  • 轻DOM是相对的。

  • An element can take part in a shadow root and in a light dom at the same time. there is no "is part of a shadow DOM OR a light DOM?"
  • 元素可以同时参与阴影根和光照。没有“是影子DOM或轻型DOM的一部分吗?”

#1


29  

If you call a ShadowRoot's toString() method, it will return "[object ShadowRoot]". According to this fact, here's my approach:

如果调用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;
}

EDIT

Jeremy Banks suggests an approach in another style of looping. This approach is a little different from mine: it also checks the passed node itself, which I didn't do.

杰里米·班克斯(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>

#2


8  

⚠️ Warning: Deprecation Risk

The ::shadow pseudo-element is deprecated in and being removed from from the dynamic selector profile. The approach below only requires that it remain in the static selector profile, but it may also be deprecated and removed there in the future. Discussions are ongoing.

不推荐使用:: shadow伪元素,并将其从动态选择器配置文件中删除。下面的方法只要求它保留在静态选择器配置文件中,但它也可能在将来被弃用并删除。讨论正在进行中。

We can use Element's .matches() method to determine if an element is attached to a shadow DOM.

我们可以使用Element的.matches()方法来确定元素是否附加到shadow DOM。

If and only if the element is in a shadow DOM, we will be able to match it by using the selector :host to identify elements that have a Shadow DOM, ::shadow to look in those shadow DOMs, and * and to match any descendant.

当且仅当元素在shadow DOM中时,我们才能通过使用selector:host来匹配它,以识别具有Shadow DOM,:: shadow以查看那些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>

#3


6  

You can check if an element has a shadow parent like this:

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

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

This uses instanceof over .toString().

这使用了ofofof .toString()。

#4


5  

Lets understand Light Dom:

让我们了解Light Dom:

The Light DOM is the user supplied DOM of an element that hosts a shadow root. For more info read at polymer-project.

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

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

This means: Light DOM is always relative to the next ancestor which hosts a shadow root.

这意味着:Light DOM始终相对于托管阴影根的下一个祖先。

An Element can be a part of the light dom of a custom element while it can be a part of the shadow root of another custom element at same time.

Element可以是自定义元素的light dom的一部分,同时它可以是另一个自定义元素的shadow root的一部分。

Example:

<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>

According to your question:

根据你的问题:

If you want to know if an element is in a shadow root, you just need to grab the element out of the document.

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

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');
}

The only Light DOM which is not preceeded by a shadow Root is part of the document, because Light DOM is relative as shown above.

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

It doesnt work backwards: if its part of the document its not in a Light DOM at all. You need to check if one of the ancestors is hosting a Shadow Root like suggested from Leo.

它不会倒退:如果它的部分文档根本不在Light DOM中。您需要检查其中一个祖先是否正在托管像Leo建议的影子根。

You can use this approach with other elements to. Just replace the "document" with e.g. "my-custom-element" and test if div#LDofMCE is in Light DOM relative to "my-custom-element".

您可以将此方法与其他元素一起使用。只需将“文档”替换为例如“my-custom-element”并测试div#LDofMCE是否在Light DOM中相对于“my-custom-element”。

Because of the lack of information about why you need this information i cant get closer...

由于缺乏有关您为什么需要这些信息的信息,我无法接近......

EDIT:

It doesnt work backwards should be understand as follows:

它不倒退应该理解如下:

Is this element in a Shadow Root?: document.contains() or the isInShadow(node) method from Leo deliver the answer.

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

"backwards" Question: Is this element in a Light DOM (In case you start searching relative to document)?: domcument.contains() does not deliver the answer because to be in a Light Dom - one of the elements ancestors needs to be a shadow host.

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

Come to the Point

来点

  • Light DOM is relative.
  • 轻DOM是相对的。

  • An element can take part in a shadow root and in a light dom at the same time. there is no "is part of a shadow DOM OR a light DOM?"
  • 元素可以同时参与阴影根和光照。没有“是影子DOM或轻型DOM的一部分吗?”