Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I tell if an element is in a shadow DOM?

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.

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.

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


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

  (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>
like image 246
Jeremy Avatar asked Dec 12 '14 23:12

Jeremy


People also ask

How do you know if an element is in Shadow DOM?

To identify Shadow DOM:Open Developer tools (press the shortcut keys Fn+F12). On the Elements tab, expand the <body> element and the first element inside the <body> element and notice the #shadow-root line.

How do you test Shadow DOM?

Create a new project. Go to File > New > Project. Here, we name the project Shadow DOM Testing. In the demo website, navigate to the search bar, right-click > Inspect.

How can you tell if a element is a DOM?

Accessing Elements by ID The easiest way to access a single element in the DOM is by its unique ID. You can get an element by ID with the getElementById() method of the document object. In the Console, get the element and assign it to the demoId variable.

What are Shadow DOM elements?

Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which you can attach any element, in the same way as the normal DOM.


2 Answers

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

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.

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>
like image 55
Leo Avatar answered Sep 28 '22 05:09

Leo


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().

like image 22
Cerbrus Avatar answered Sep 28 '22 04:09

Cerbrus