如何克隆ShadowRoot?

Vix*_*Vix 17 html javascript html5 dom shadow-dom

我正在尝试克隆一个影子根,以便可以将<content></content>它们的实例与它们相应的分布式节点交换。

我的方法:

var shadowHost = document.createElement('div');
var shadowRoot = shadowHost.createShadowRoot();

var clonedShadowRoot = shadowRoot.cloneNode(true);
Run Code Online (Sandbox Code Playgroud)

不起作用,因为“ ShadowRoot节点不可克隆”。

这样做的动机是希望检索组成的阴影树,以便可以使用呈现的HTML标记。

由于Shadow DOM的性质,这可能不起作用,对克隆节点的引用很可能会破坏对分布式节点的引用。

组成影子树可能是本机功能,但是在搜索了w3c规范后,我找不到这种方法。

有这样的本机方法吗?否则,手动遍历(在过程中复制树)会起作用吗?

ben*_*nvc 5

如果您尝试深度克隆可能包含一个或多个嵌套影子树的节点,那么您将需要从该节点遍历 DOM 树并沿途检查影子根。如果对之前提出了有缺陷的浅克隆方法的答案感兴趣,请参阅编辑历史记录。

const deepClone = (host) => {
  const cloneNode = (node, parent) => {
    const walkTree = (nextn, nextp) => {
      while (nextn) {
        cloneNode(nextn, nextp);
        nextn = nextn.nextSibling;
      }
    };
    
    const clone = node.cloneNode();
    parent.appendChild(clone);
    if (node.shadowRoot) {
      walkTree(node.shadowRoot.firstChild, clone.attachShadow({ mode: 'open' }));
    }
  
    walkTree(node.firstChild, clone);
  };
  
  const fragment = document.createDocumentFragment();
  cloneNode(host, fragment);
  return fragment;
};

// Example use of deepClone...

// create shadow host with nested shadow roots for demo
const shadowHost = () => {
  const host = document.createElement('div');
  const nestedhost = document.createElement('p');
  nestedhost.attachShadow({mode: 'open'}).appendChild(document.createElement('span'));
  host.attachShadow({mode: 'open'}).appendChild(nestedhost);
  return host;
};

// return fragment containing deep cloned node
const fragment = deepClone(shadowHost());
// deep cloned node
console.log(fragment.firstChild); 
// shadow tree node
console.log(fragment.firstChild.shadowRoot.firstChild);
// nested shadow tree node
console.log(fragment.firstChild.shadowRoot.firstChild.shadowRoot.firstChild);
Run Code Online (Sandbox Code Playgroud)


Int*_*lia 2

好的。这有点疯狂,但这是我编写的一个例程,它将克隆 ShadowRoot 的子级。这符合 V1 规范。

function cloneShadow(shadow) {
  const frag = document.createDocumentFragment();

  var nodes = [...shadow.childNodes];
  nodes.forEach(
    node => {
      node.remove();
      frag.appendChild(node.cloneNode(true));
      shadow.appendChild(node);
    }
  );

  return frag;
}

const s1 = document.querySelector('.shadow1');
const s2 = document.querySelector('.shadow2');

s1.attachShadow({mode:'open'}).innerHTML = `<h1>Header</h1>
<p>Content in a paragraph</p><slot></slot>`;

setTimeout(() => {
  s2.attachShadow({mode:'open'}).appendChild(cloneShadow(s1.shadowRoot));}, 1000);
Run Code Online (Sandbox Code Playgroud)
.shadow1 {
  background-color: #F88;
}

.shadow2 {
  background-color: #88F;
}
Run Code Online (Sandbox Code Playgroud)
<div class="shadow1">
  <p>SHADOW 1</p>
</div>
<div class="shadow2">
  <p>SHADOW 2</p>
</div>
Run Code Online (Sandbox Code Playgroud)

我必须从shadowDOM 中删除每个节点,然后克隆它,然后将其附加回shadowRoot。

我什至添加了一个,setTimeout以便您可以随时看到它是否有效。

它甚至可以与插槽一起使用。

  • 当它们仍处于 ShadowRoot 中时,它无法克隆它们。因此,删除它们然后重新附加它们似乎是让它发挥作用的唯一方法。 (2认同)