Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I clone a ShadowRoot?

I'm trying to clone a shadow root, so that I may swap instances of <content></content> with their corresponding distributed nodes.

My approach:

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

var clonedShadowRoot = shadowRoot.cloneNode(true);

does not work, as "ShadowRoot nodes are not clonable."

The motivation for this is that I wish to retrieve the composed shadow tree, so that I may use the rendered HTML markup.

This may not work due to the nature of the Shadow DOM, the reference to the distributed nodes is likely to be broken by the cloning process.

Composing the shadow tree is likely to be a native feature, but having searched through the w3c spec, I was unable to find such a method.

Is there such a native method? Or, failing that, would manual traversal (replicating the tree in the process), work?

like image 459
Vix Avatar asked Nov 27 '14 11:11

Vix


People also ask

How do you get rid of shadow roots?

You can't remove a shadow root once you add it. However, you can replace it with a newer one. As mentioned here, http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/, the newest shadow root "wins" and becomes the rendered root.

How does Shadow DOM work?

Shadow DOM allows hidden DOM trees to be attached to elements in the regular DOM tree — this shadow DOM tree starts with a shadow root, underneath which you can attach any element, in the same way as the normal DOM.

Can I use declarative Shadow DOM?

Declarative Shadow DOM can be used on its own as a way to encapsulate styles or customize child placement, but it's most powerful when used with Custom Elements. Components built using Custom Elements get automatically upgraded from static HTML.


1 Answers

If you are trying to deep clone a node that may contain one or more nested shadow trees, then you will need to walk the DOM tree from that node and check for shadow roots along the way. See edit history if interested in the previous answer that suggested a flawed approach to shallow cloning.

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);
like image 84
benvc Avatar answered Oct 03 '22 11:10

benvc