Adoption procedure

Custom elements benefit from lifecycle callbacks, functions executed during the element’s lifetime, and in a short exploration I tried to entertain the available options. I also failed to find an application for one in particular, adoptedCallback, and to be thorough, decided to revisit the concept with an example. A self-serving example, but if nothing, amusing as well.

adoptedCallback lives as a method on the class defined for your own made-up element.

class ChubbyCucco extends HTMLElement {
	// ...constructor
	adoptedCallback(oldDocument, newDocument) {}
}

Per the specification, the function runs when the element moves between documents, receiving the old and the new context as arguments.

You are acquainted with one document already, the page where the custom element lives.

<chubby-cucco></chubby-cucco>

For a separate document in the same scope you can lean on the iframe element.

<!-- ...chubby-cucco -->
<iframe title="Chicken coop" width="300" height="300"></iframe>

An iframe works as a window to a separate resource, one you would reference with the src attribute, but it is possible to omit the reference altogether, and add content programmatically. This means we can leave off the attribute and satisfy the browser with a title and the measure for the width and the height.

From this setup, the idea is to use JavaScript to move the element to the iframe, adopting the node in the new document.

Pertintently, document.adoptNode takes an element as its first and only argument, moving the node from the current document and returning the orphan child. Once you identify the custom element, you are therefore able to carry out the first half of the transaction.

const chubbyCucco = document.querySelector('chubby-cucco');
const orphanCucco = document.adtopNode(chubbyCucco);

To complete the operation, you can access the iframe’s document with contentDocument. Think of this entity as a veritable document, a new index.html file with its own information in terms of metadata and content. The content resides in the document’s body, so that similarly to the existing page, you can add the orphan child through the appendChild function.

const iframe = document.querySelector('iframe');
iframe.contentDocument.body.appendChild(node);

There are a couple of steps to finalize the transfer, but as you run the script, the custom element moves between documents. And in the process three lifecycle functions follow one another:

  • disconnectedCallback, the element is removed from the DOM, or at least the DOM of the old document

  • adoptedCallback, the element is moves between documents

  • connectedCallback, the element is inserted in the DOM, that of the separate iframe

In the impromptu adoptedCallback you gain the option of updating the custom element, in a manner fitting of the unlikely scenario.

You might be wondering whether the code applies to standard DOM nodes, whether it is possible to adopt a non-custom element and move it into an iframe. It is indeed possible, but — maniacally checks the docs for the umpteenth time — there isn’t an equivalent event on the nodes. Something to consider if you pick up the code.