问题
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)?
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/40004064/how-do-we-detect-if-a-shadow-root-was-made-with-v0-or-v1-api