Shadow DOMをネイティブに使用しているプロジェクトがあります(ポリフィルではありません)。特定のelement
がシャドウDOMまたはライトDOMのどちらに含まれているかを検出したいと思います。
要素のすべてのプロパティを調べましたが、要素が含まれているDOMのタイプによって異なるプロパティはないようです。
要素がシャドウDOMの一部であるかライトDOMの一部であるかをどのように判断できますか?
これは、この質問の目的で「シャドウDOM」および「ライトDOM」と見なされるものの例です。
(ライトルート)•ドキュメント (ライト)•HTML (ライト)| •BODY (ライト)| •DIV (シャドウルート)| •ShadowRoot (シャドウ)| •DIV (影)| •IFRAME (ライトルート)| •ドキュメント (ライト)| •HTML (ライト)| | •BODY (ライト)| | •DIV (シャドウルート)| | •ShadowRoot (シャドウ)| | •DIV (なし)| •[2番目のドキュメントの未接続DIV] (なし)•[最初のドキュメントの未接続DIV]
<!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>
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;
}
[〜#〜]編集[〜#〜]
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>
⚠️警告:非推奨のリスク
_
::shadow
_疑似要素 非推奨であり、動的セレクタープロファイルから削除されています 。以下のアプローチでは、静的セレクタープロファイルに残る必要があるだけですが、mayも非推奨になり、将来的に削除される可能性があります。 議論は進行中です 。
Element
's .matches()
method を使用して、要素がShadowDOMにアタッチされているかどうかを判別できます。
要素がShadowDOM内にある場合にのみ、セレクター_: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>
_
要素に次のようなシャドウの親があるかどうかを確認できます。
_function hasShadowParent(element) {
while(element.parentNode && (element = element.parentNode)){
if(element instanceof ShadowRoot){
return true;
}
}
return false;
}
_
これは、.toString()
ではなくinstanceof
を使用します。
Light Domを理解しましょう:
Light DOMは、シャドウルートをホストする要素のユーザー提供のDOMです。詳細については、polymer-projectを参照してください。
https://www.polymer-project.org/platform/shadow-dom.html#shadow-dom-subtrees
つまり、LightDOMは常にrelative to シャドウルートをホストする次の祖先 。
要素は、カスタム要素のライトドームの一部にすることができますが、別のカスタム要素のシャドウルートの一部にすることもできます同時に。
例:
<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>
あなたの質問によると:
要素がシャドウルートにあるかどうかを知りたい場合は、ドキュメントから要素を取得する必要があります。
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');
}
ライトDOMは上記のように相対的であるため、シャドウルートが先行しない唯一のライトDOMはドキュメントの一部です。
逆方向には機能しません:ドキュメントの一部がLightDOMにまったく含まれていない場合。 Leoから提案されているように、祖先の1つがシャドウルートをホストしているかどうかを確認する必要があります。
このアプローチを他の要素と一緒に使用できます。 「ドキュメント」をたとえば次のように置き換えるだけです。 「my-custom-element」とテストしてdiv#LDofMCE
は「my-custom-element」に関連するLightDOMにあります。
なぜこの情報が必要なのかについての情報が不足しているため、近づきません...
編集:
逆方向には機能しません次のように理解する必要があります。
この要素はシャドウルートにありますか?:document.contains()またはLeoのisInShadow(node)メソッドが答えを提供します。
「後方」質問:この要素はLightDOMにありますか(ドキュメントに関連して検索を開始する場合)?:domcument.contains()は、LightDomにあるため回答を提供しません-1つ要素の祖先はシャドウホストである必要があります。
要点を説明する