Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if element contains #shadow-root

Is it possible to see if a Shadow DOM element exists? I'm not too concerned with manipulating it, or even really targeting it per-say. I understand the reasoning of the encapsulation. But I'd like to be able to style other elements in the regular DOM, based on whether or not the Shadow DOM element is present.

Sort of like:

if ( $('#element-id #shadow-root').length ) {
    // true
}

Or if not for the shadow-root, at least a specific element within, like the id of a div. So if that div exists, then clearly that Shadow DOM element is on the page.

I know it wouldn't be that simple... From some research I've done, there are things like >>> and /deep/ but their support seems to be low/none/deprecated. Buy maybe there's another way, however inelegant it may be?

like image 874
Chase Avatar asked Feb 13 '16 02:02

Chase


3 Answers

If you want to check whether or not a specific element is hosting an open Shadow DOM element, you can do the following:

var el = document.querySelector('#some-element');
if (!!el.shadowRoot) {
    // Then it is hosting an OPEN Shadow DOM element
}

You can also get the Shadow DOM element, and then operate on it like a normal node:

var shadowEl = el.shadowRoot;
// And for example:
console.log(shadowEl.innerHTML);

Here is an example that works in the latest version of Chrome:

const div = document.querySelector('div');
const p = document.querySelector('p');

const shadowRoot = p.attachShadow({mode: 'open'})
shadowRoot.textContent = 'A Shadow DOM Paragraph. I overrode the content specified!';

console.log('Paragraph has Shadow DOM:', !!p.shadowRoot); // true
console.log('Div has Shadow DOM:', !!div.shadowRoot); // false
<div>A Normal Div</div>
<p>A Normal Paragraph</p>
like image 103
KevBot Avatar answered Oct 18 '22 19:10

KevBot


You can access the shadowRoot of an element with the property shadowRoot, so you could traverse all the nodes and check if the property is null or not.

You can select all nodes in a document with document.getElementsByTagName('*').

So all in all, we would have something like this:

var allNodes = document.getElementsByTagName('*');
for (var i = 0; i < allNodes.length; i++) {
  if(allNodes[i].shadowRoot) {
    // Do some CSS styling
  }
}

With the additions of ES6, we could do something simpler like this:

document.getElementsByTagName('*')
    .filter(element => element.shadowRoot)
    .forEach(element => {
        // Do some CSS styling
    });
like image 43
Marko Kajzer Avatar answered Oct 18 '22 17:10

Marko Kajzer


The other answers by KevBot and Marko Kajzer only work for ShadowRoot created with mode: 'open'. Here's a way to detect if an element has a ShadowRoot, even if the root is closed. Make sure this runs before other code (before any calls to attachShadow) or it will fail to catch any elements that already have a ShadowRoot by the time this code is set up:

const shadowHosts = new WeakSet()

const original = Element.prototype.attachShadow

Element.prototype.attachShadow = function attachShadow(...args) {
    const result = original.apply(this, args)

    shadowHosts.add(this)

    return result
}

export function hasShadow(el) {
    return shadowHosts.has(el)
}

then use hasShadow on any element

if (hasShadow(someElement)) {...}
like image 1
trusktr Avatar answered Oct 18 '22 19:10

trusktr