Suppose a JS module exports shadowRoot
which was created with either el.createShadowRoot
or el.attachShadow
(we don't know which). How do we detect if the root is a v0 shadow root or a v1 shadow root (i.e. how do we detect which method was used to create the root)?
f.e., What would I fill in the following conditional statements?
// for argument's sake, we don't create the root, we only get a reference
// to it:
import shadowRoot from 'somewhere'
function getShadowRootVersion(root) {
if ( ... )
return 'v0'
if ( ... )
return 'v1'
}
console.log(getShadowRootVersion(shadowRoot)) // should output "v0" or "v1".
More info:
We want to find out if a shadow root was created from createShadowRoot
or from attachShadow
. The resulting roots are different: in the root created with createShadowRoot
, <content>
elements are used for distributing elements. In roots created with attachShadow
, <content>
elements don't do anything, and <slot>
elements are used instead. How do we detect which method was used to create a root (i.e. whether we have a v0 root or a v1 root)?
I went a similar direction to Hayato Ito's answer. However, instead of creating slot
elements, I target content
elements. I was not able to find a way to detect the version off of any API method detection.
I targeted content
elements since content elements do not natively have events for them, unlike slotchange
on the slot
event, which hopefully could lead to a small performance boost. Plus the function returns v1
a little faster if the browser does not support v0
at all.
function shadowType(shadowRoot) {
if (!shadowRoot) {
// closed shadow dom does not appear to have a shadowRoot...
// It could be assumed that it is v1, but for now return undefined
return;
}
const content = document.createElement('content');
// In browsers that support v1, but not v0 (ex: Safari)
if (!content.getDistributedNodes) {
return 'v1';
}
content.setAttribute('select', 'test-shadow-dom-version');
shadowRoot.appendChild(content);
const testElement = document.createElement('test-shadow-dom-version');
shadowRoot.host.appendChild(testElement);
const type = (content.getDistributedNodes().length) ? 'v0' : 'v1';
shadowRoot.removeChild(content);
shadowRoot.host.removeChild(testElement);
return type;
}
It definitely feels like a "hack", because of need to append random dom :(. I did test this in Chrome, Firefox, Safari, IE11, and Edge. I tested components made using the webcomponentsjs (v0) polyfill, and it correctly returned v0
for each component. I also tested those same browsers with just the shadydom (v1) polyfill with components created with the v1 spec, and received v1
in all of those browsers.
The following hack should work:
function isV1(shadowRoot) {
const slot = document.createElement('slot');
shadowRoot.appendChild(slot);
slot.appendChild(document.createElement('div'));
const assignedNodes = slot.assignedNodes({ flatten: true });
slot.remove();
return assignedNodes.length !== 0;
}
IMO, there is something wrong when you have to detect it.