There’s an interesting DOM feature that I just came across that’s a method of the document
object that allows you to remove elements from an <iframe>
that’s embedded on a page and drop them into the current page (or vice versa).
In other words, you adopt the elements from the child frame into the parent. The code for document.adoptNode()
looks like this:
document.adoptNode(node)
Where node
is equal to the node you want to bring in from the other document.
So let’s say I have a document called page.html that looks like this:
<body>
<p class="one">One</p>
<p class="two">Two</p>
<p class="three">Three</p>
</body>
All I have here is a body element with three uniquely-classed paragraphs. Nothing special.
I’m going to embed that page as the source of an iframe inside another document that looks like this:
<iframe src="page.html"></iframe>
<p><button>ADOPT THE ELEMENTS!</button></p>
<div class="container"></div>
The button in this parent page will be used to trigger the action. Here’s the JavaScript:
let myFrame = document.querySelector('iframe'),
myPars = myFrame.contentDocument.querySelectorAll('p'),
container = document.querySelector('.container');
document.querySelector('button').addEventListener('click', function() {
myPars.forEach(function(par) {
container.appendChild(document.adoptNode(par));
});
}, false);
To sum up the main parts of the above code:
- I’m using the
contentDocument
property of the iframe object to get access to that frame’s document - I’m looping through the paragraphs using
forEach
- Each paragraph is adopted individually into the parent document, each one appended inside
.container
Try it by visiting the demo below.
On the demo page, just click the button and you’ll see the three paragraphs disappear from the iframe and appear on the parent page. This works differently from the more commonly-known importNode()
method, which more or less does the same thing, except the imported nodes stay in the original document (cloning them rather than moving them).
Re-Adopting Elements from the Parent
Now I’ll take this even further by using a second embedded iframe (page2.html) to pull the same paragraphs out of the parent frame and into the second frame. Here’s the code that I’ll run inside page2.html:
let container = document.querySelector('.container'),
myPars = parent.document.querySelectorAll('p[class]');
document.querySelector('button').addEventListener('click', function() {
myPars.forEach(function(par) {
container.appendChild(document.adoptNode(par));
});
}, false);
Here I’m doing the following:
- Accessing the parent frame using
parent.document
- Grabbing the newly adopted paragraphs (using the
class
attribute to limit which ones I grab in myquerySelector
call) - Re-adopting the paragraphs into the second iframe’s document using the same kind of loop as the previous example
In this demo you’ll have to first adopt the nodes into the parent then use the second frame’s button to re-adopt them into that frame.
The class names that I originally included on the paragraphs don’t mean anything. I simply wanted to demonstrate that the elements are moved exactly as they appear in the original page. If you inspect the second frame with your developer tools, you can see the paragraphs pulled in with the class names intact, as shown in the following screenshot:
Since these scripts are working across frames, this sort of thing would be subject to the usual cross-origin limitations. So if the child frame page is not on the same origin, the server would have to enable cross-origin resource sharing (or CORS) for that page, otherwise you’ll get an error in your console indicating a cross-origin limitation.
Use Cases for This?
The first thing that comes to mind where this might be useful is in a game context. For example a game that embeds another page in an iframe that allows the user to interact with the iframe, possibly moving items from one frame to the other.
Another example might be some kind of grid or spreadsheet-like app that embeds various iframes and maybe some Ajax keeps track of nodes that are moved from one page to the other, essentially saving the content wherever it’s “adopted”.
Finally, if you’re wondering about browser support, this works just about everywhere, including IE9 and up, so this is an older feature that has pretty wide support.
If you have any ideas on how this might be useful, I’d love to hear your comments.
What would happen if I just appended these paragraphs to the parent page directly, without passing them to `document.adoptNode` first?
I believe the primary difference there would be that they would not disappear from the original document. So this seems to be a quick way to not only add them to the new document, but also remove them without having to use a different method to delete them first.
Iframes bad for SEO, I observed and the giant search engine rules avoid the use of frames in websites what would you say?
So, could you have hidden iframes from various sources and use this to aggregate the content into a parent document, for example, gathering news headlines?
Does this get around the cross origin issues you’d get with XHR?
No, as I mention in the article:
So you could only really do it with your own content or content that allowed you access.
Iframes are not good for seo,search engine factors doesn’t allow the frames in websites.Is that correct?
Hello Louis, Great tutorial.
I want to use your script for selecting picto’s in the iframe.
I have tried to place a button in the iframe for calling the function in the parentpage ,because i need to select pictograms directly from the iframe.
Sadly i have been unable to make it work.
Do you think it’s possible?
You can see the example on:
http://gebaren-taal.nl/ben/levi/dagplanner/node/4/node.html
Thank you. Toby van Zanten
I don’t think it’s possible to do it the way you want.
In your example, it works if you click the first iframe button, then the second iframe button, but not if you do it the other way around.
From SEO Prospect, Iframe is not good.