我在Chrome Canary(33.0.1712.3)中探索导入,模板,影子DOM和自定义元素。在网格布局中,我有一个特定的内容元素(显示区域),它将显示从文件导入的不同Web组件或克隆的轻型DOM片段。但是,一旦添加了阴影DOM,我就无法重新显示普通的HTML DOM,因为我不知道如何删除阴影根。一旦创建,阴影根就会保留并干扰普通DOM的呈现。 (我已经查看了各种W3C规范,例如Web组件介绍,影子DOM,模板,Bidelman关于HTML5 Rocks的文章等)。我在下面的一个简单例子中解决了这个问题:
点击“show plain old div”;点击“显示阴影模板”;点击“show plain old div”。每次点击后检查devtools。第三次点击后,按钮下面没有输出,我看到的是devtools:
<div id="content">
#document-fragment
<div id="plaindiv">Plain old div</div>
</div>
我需要添加什么来移除removeShadow()以移除阴影根并将内容元素完全重置为其初始状态?
removing_shadows.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<template id="shadowedTemplateComponent">
<style>
div { background: lightgray; }
#t { color: red; }
</style>
<div id="t">template</div>
<script>console.log("Activated the shadowed template component.");</script>
</template>
<template id="plainDiv">
<div id="plaindiv">Plain old div</div>
</template>
</head>
<body>
<div>
<input type="button" value="show plain old div" onclick="showPlainOldDiv()"/>
<input type="button" value="show shadowed template" onclick="showShadowTemplate()"/>
<div id="content"></div>
</div>
<script>
function removeChildren(elt) {
console.log('removing children: %s', elt);
while (elt.firstChild) {
elt.removeChild(elt.firstChild);
}
}
function removeShadow(elt) {
if (elt.shadowRoot) {
console.log('removing shadow: %s', elt);
removeChildren(elt.shadowRoot); // Leaves the shadow root property.
// elt.shadowRoot = null; doesn't work
// delete elt.shadowRoot; doesn't work
// What goes here to delete the shadow root (#document-fragment in devtools)?
}
}
function showPlainOldDiv() {
console.log('adding a plain old div');
var host = document.querySelector('#content');
removeChildren(host);
removeShadow(host);
var template = document.querySelector('#plainDiv');
host.appendChild(template.content.cloneNode(true));
}
function showShadowTemplate() {
console.log('adding shadowed template component');
var host = document.querySelector('#content');
removeChildren(host);
removeShadow(host);
var template = document.querySelector('#shadowedTemplateComponent');
var root = host.shadowRoot || host.webkitCreateShadowRoot();
root.appendChild(template.content.cloneNode(true));
}
</script>
</body>
</html>
答案 0 :(得分:4)
Shadow DOM的规格从v0移动到v1。
其中一个变化是在v1中无法在自身上创建阴影根,并且主机元素可能只包含一个阴影根。
所以似乎用新的空白阴影根替换阴影根的答案已经无效了。
答案 1 :(得分:0)
添加后,您无法删除阴影根。但是,您可以用更新的替换它。
如前所述,http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/,最新的影子根“胜出”并成为渲染的根。
您可以使用仅包含<content>
伪元素的新影子根替换您的影子根,以将来自light DOM的所有内容插入到影子DOM中。那时,据我所知,它在功能上等同于根本没有影子DOM。
答案 2 :(得分:0)
rmcclellan是正确的,您不能真正“删除” ShadowRoot v2。但是,您可以伪造它。
elementWithShadowDOMv2.outerHTML = elementWithShadowDOMv2.outerHTML;
但是,有一个主要警告:尽管没有视觉上的变化,elementWithShadowDOMv2
仍然使用ShadowDOMv2引用了被破坏的元素,就像调用了elementWithShadowDOMv2.parentNode.removeChild( elementWithShadowDOMv2 )
一样。这也会“删除”元素上的事件侦听器。观看下面的演示。
var addShadowHere = document.getElementById("add-shadow-here");
addShadowHere.addEventListener("mouseenter", function() {
addShadowHere.style.border = '2em solid blue';
});
addShadowHere.addEventListener("mouseleave", function() {
addShadowHere.style.border = '';
});
var shadow = addShadowHere.attachShadow({mode:"open"});
var button = shadow.appendChild(document.createElement("button"));
button.textContent = "Click Here to Destroy The ShadowDOMv2";
button.addEventListener("click", function() {
addShadowHere.outerHTML = addShadowHere.outerHTML;
update();
});
update();
function update() {
// This just displays the current parent of the addShadowHere element
document.getElementById("parent-value").value = "" + (
addShadowHere.parentNode &&
addShadowHere.parentNode.cloneNode(false).outerHTML
);
}
<div id="add-shadow-here">Text Hidden By Shadow DOM</div>
addShadowHere.parentNode => <input readonly="" id="parent-value" />
请注意,删除ShadowDOM后,蓝色边框如何停止工作。这是因为事件侦听器不再在新元素上注册:事件侦听器仍在已从DOM中删除的旧元素上注册。
因此,您必须刷新对元素的所有引用并重新附加任何事件侦听器。这是如何重新获得对新元素的引用的示例。
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var parent = elementWithShadow.parentNode;
var prior = elementWithShadow.previousSibling;
elementWithShadow.outerHTML = elementWithShadow.outerHTML;
return prior.nextSibling || parent.firstChild;
}
如果您需要访问现有影子根源自然隐藏的元素,并且这些元素将在影子根源被驱除后暴露出来,那么这是一种可以完全保留这些节点的替代方法。
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var ref = elementWithShadow.cloneNode(true);
while (elementWithShadow.lastChild) ref.appendChild( elementWithShadow.lastChild );
elementWithShadow.parentNode.replaceChild(elementWithShadow, elementWithShadow);
return ref;
}
var createShadowProp = (
"createShadowRoot" in Element.prototype ? "createShadowRoot" : "webkitCreateShadowRoot"
);
function removeChildren(elt) {
console.log('removing children: %s', elt);
while (elt.firstChild) {
elt.removeChild(elt.firstChild);
}
}
function removeShadowWithCaveat(elementWithShadow) {
if (!elementWithShadow.parentNode) return elementWithShadow.cloneNode(true);
var ref = elementWithShadow.cloneNode(true);
while (elementWithShadow.lastChild) ref.appendChild( elementWithShadow.lastChild );
elementWithShadow.parentNode.replaceChild(elementWithShadow, elementWithShadow);
return ref;
}
function showPlainOldDiv() {
console.log('adding a plain old div');
var host = document.querySelector('#content');
removeChildren(host);
// Remove the shadow
host = removeShadowWithCaveat(host);
var template = document.querySelector('#plainDiv');
host.appendChild(template.content.cloneNode(true));
}
function showShadowTemplate() {
console.log('adding shadowed template component');
var host = document.querySelector('#content');
removeChildren(host);
// Remove the shadow
host = removeShadowWithCaveat(host);
var template = document.querySelector('#shadowedTemplateComponent');
var root = host.shadowRoot || host[createShadowProp]({
"open": true
});
root.appendChild(template.content.cloneNode(true));
}
<div>
<input type="button" value="show plain old div" onclick="showPlainOldDiv()"/>
<input type="button" value="show shadowed template" onclick="showShadowTemplate()"/>
<div id="content"></div>
</div>
<template id="shadowedTemplateComponent" style="display:none">
<style>
div { background: lightgray; }
#t { color: red; }
</style>
<div id="t">template</div>
<script>console.log("Activated the shadowed template component.");</script>
</template>
<template id="plainDiv" style="display:none">
<div id="plaindiv">Plain old div</div>
</template>
还请注意供应商前缀的滥用(这个问题是太多开发人员遇到的问题)。您是正确的,在提出此问题时,只有createShadowRoot
的前缀版本(即webkitCreateShadowRoot
)。但是,如果将来浏览器将API标准化(现在是这种情况),则必须始终检查未加前缀的createShadowRoot
版本是否可用。让您的代码在今天工作可能会很好,但从现在起几年后再运行代码也很棒。