DEV Community

Kushagra Gour
Kushagra Gour

Posted on

Sending a DOM Element over postmessage

I had a requirement where I wanted to send a DOM Element through postMessage. If you have tried this before, you know that it can't be done as DOM Elements are not clonable or serializable to be able to send in postMessage. So I got down to writing a function that converts a DOM Element into something like a DOM Element which I can send over postMessage.

Note: For my use case, I finally wanted an object on which I could access trivial things like tagName, childNodes, nodeType etc. So if I get those properties on my fake DOM Element, I was good.

The small little function

function getFakeDomElement(node) {
    const fakeNode = {
        nodeName: node.nodeName,
        nodeType: node.nodeType,
        tagName: node.tagName,
        childNodes: [...node.childNodes].map(child => getFakeDomElement(child)),
        textContent: node.textContent
    }
    if(node.attributes) {
        fakeNode.attributes = [...node.attributes].map(attribute => ({name:attribute.name, value:attribute.value}))
    }
    return fakeNode;
}

Let me brief out what this does.

  • Firstly, this is a recursive function. Meaning, it calls itself for the child nodes. Refer the line [...node.childNodes].map(child => getFakeDomElement(child)).
  • We are creating a new object for every node and simply copying all the required properties on our new node.
  • Special treatment is required for childNodes and attributes because they can't simply be copied. They are the reason why our DOM Element wasn't clonable in the first place.
  • For childNodes, it's easy. We just run our getFakeDomElement function over each child node to get a fake child node.
  • For attributes, if we find any, we map it into an array of objects. Each object is of the structure {name, value} - similar to how attributes can be accessed.

And we are done! We finally get a fake DOM Element which we can iterate and access just like a DOM Element, that too over postMessage.

Follow me on twitter for more such posts and my journey as an Indiependent Product Maker.

Top comments (1)

Collapse
 
peteichuk profile image
Michael

You can use innerHTML for sending a DOM Element then parse it in popup.

send html element:

const popup = window.open();
popup.postMessage({
  htmlAttr: window.document.body.parentNode.getAttributeNames().reduce((acc, attr) => {
    acc[attr] = window.document.body.parentNode.getAttribute(attr);
    return acc;
  }, {}),
  clonedDom: document.body.parentNode.innerHTML
});

popup.addEventListener("message", (event) => {
  console.log('event from popup', event);
}, false);
Enter fullscreen mode Exit fullscreen mode

then get it in popup html element:

window.addEventListener("message", (event) => {
  console.log('event from parent', event);
  var htmlAttr = event.data.htmlAttr;
  var clonedDom = event.data.clonedDom;

  var html = document.createElement('html');
  Object.keys(htmlAttr).forEach((attr) => html.setAttribute(attr, htmlAttr[attr]));
  html.innerHTML = clonedDom;

  console.log(html); // cloned full html page from parent
  window.postMessage('html element has been send'); // callback
}, false);
Enter fullscreen mode Exit fullscreen mode

You can send any node of DOM element with innerHTML