我有一个项目,我本地使用shadow DOM(不是通过polyfill)。我想检测给定的element
是否包含在影子DOM或轻型DOM中。
我已经查看了元素的所有属性,但似乎没有根据元素所在的DOM类型而有所不同。
如何确定元素是影子DOM还是轻型DOM的一部分?
这是一个被考虑的例子" shadow DOM"和"轻DOM"为了这个问题的目的。
(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>
&#13;
答案 0 :(得分:29)
如果您调用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>
答案 1 :(得分:8)
⚠️警告:弃用风险
::shadow
伪元素is deprecated in and being removed from from the dynamic selector profile。下面的方法只要求它保留在静态选择器配置文件中,但可能也将被弃用并在将来删除。 Discussions are ongoing。
我们可以使用Element
's .matches()
method来确定元素是否附加到影子DOM。
当且仅当元素在阴影DOM中时,我们才能通过使用选择器:host
来匹配它,以识别具有阴影DOM的元素,::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>
答案 2 :(得分:6)
您可以检查元素是否具有如下阴影父级:
function hasShadowParent(element) {
while(element.parentNode && (element = element.parentNode)){
if(element instanceof ShadowRoot){
return true;
}
}
return false;
}
这比instanceof
使用.toString()
。
答案 3 :(得分:5)
让我们了解Light Dom:
Light DOM是用户提供的托管阴影根的元素的DOM。 欲了解更多信息,请阅读聚合物项目。
https://www.polymer-project.org/platform/shadow-dom.html#shadow-dom-subtrees
这意味着:Light DOM 总是 相对 到托管阴影根的下一个祖先。< / p>
元素可以是自定义元素的光源的一部分,而它可以是另一个自定义元素同时的影子根的一部分。
示例:强>
<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');
}
唯一没有影子Root的Light DOM是文档的一部分,因为Light DOM是相对的,如上所示。
它不向后工作:如果它的部分文档根本不在Light DOM中。您需要检查其中一个祖先是否正在托管阴影根,如 Leo 中建议的那样。
您可以将此方法与其他元素一起使用。只需更换&#34;文件&#34;与... &#34;我的定制元素&#34;并测试{DOM}相对于&#34; my-custom-element&#34;是否div#LDofMCE
。
由于缺乏有关您需要此信息的原因的信息,我无法接近......
修改强>
它不能倒退应该理解如下:
这个元素是阴影根吗?:document.contains()或来自Leo的isInShadow(节点)方法提供答案。
&#34;向后&#34;问题:这个元素是否在Light DOM中(如果你开始相对于文档搜索)?:domcument.contains()没有提供答案,因为它位于Light Dom中 - 其中一个元素祖先需要成为影子主人。
来点
答案 4 :(得分:0)
可以使用getRootNode
来找到shadowDOM中的元素,如下所示。
function isInShadow(node) {
return node.getRootNode() instanceof ShadowRoot;
}