Creating a pop-out iframe with adoptNode and "magic iframes"

Update: 8th October - Significant issues with this doc.

I caught up with Jake Archibald about this post because I thought I had something novel, during the conversation we uncovered a lot of things that make some of this post invalid, and I also learnt a lot in the process that I don’t think most developers know.

  • Calling .append() and .appendChild() adopt the node. This makes the usage of adoptNode in this instance useless because the append Algorithm ensures that the node is adopted. This wasn’t mentioned in MDN docs, but is in the spec. I need to go back and workout why I had an issue earlier, but I suspect it was because I was orginally trying to append a DocumentFragment. This means that both w.document.body.appendChild(document.adoptNode(airhornerIframe)); and w.document.body.appendChild(airhornerIframe); will have the same effect.
  • Whilst DOM elements will keep their state (check the custom element), if an iframe is moved in the DOM it is reloaded. Period. This means that moving it between iframes will not keep the state like I had originally tested, I believe this was due to the fact that the SW loaded the page incredibly quickly. The portals API might not be affect by this - so in the future this experience should work :)

The concept of moving elements between documents is still valid and interesting, but the benefit to iframes isn’t there. I noticed that video elements got reset when moved between windows and I should have been more diligent verifying the iframe didn’t actually reset it’s state.

As always, you can see the commit history for this post.

Original post

When I joined Google in 2010 I stumbled across a document that mentioned a concept in gmail called ‘magic iframes’, it had a cool name and the concept was novel.

  • Targeted at apps with multiple windows
  • All code and data go into an IFRAME
  • If window hosting the IFRAME unloads, it gets adopted by another of the windows
  • In Gmail for example:
    • Tearoff / pop-out compose creates bare window that is filled by code in IFRAME in main window
    • If you close the main window, the code looks for a tear-off that can accept the IFRAME and moves it
    • You finish your compose and can still send the email
  • Old way: create new instance of Gmail tailored to the task.

The concept is that many applications have to load lots of complex JavaScript even for a ‘small component’ like the compose window in gmail, you could have the components of the application loaded in an iframe that the user can interact with in the main window, that you could then ‘tear off’ and move to a new window when the uses clicks ‘compose in new window’ button. I wasn’t confident enough to speak to the author (and I still haven’t, nor have I looked at the source for gmail to see if it was ever actually used) but it did stay in my mind mostly because the name was enigmatic.

Hop forwards 10 years and I was on a long train ride and started to investigate an area that I don’t know much about the adoptNode API. I played with a lot of ideas and I realised that it’s possible to move DOM elements, their current state and their attached event handlers into new windows. This reminded me of ‘magic iframes’ and ultimately lead to the idea that you can a create a pop-out iframe (A pop-out iframe is Picture in Picture video but for iframe elements)

The code for the pop-out iframe is pretty simple:

<iframe src="https://airhorner.com" id="airhorner"> </iframe>

<button id="adoptIframeButton">
  Popout iframe into new window (adoptNode)
</button>

<script>
 adoptIframeButton.addEventListener("click", () => {
    const airhornerIframe = document.getElementById("airhorner");
    const width = airhornerIframe.clientWidth;
    const height = airhornerIframe.clientHeight;
    const w = window.open("blank.html", "", `top=100,width=${width},height=${height}`);
    w.addEventListener("load", () => {
      w.document.body.appendChild(airhornerIframe);
    });
 });
</script>

adoptNode allows you to move DOM elements with their current state while maintaining their existing bound event handlers, between documents in the browser - that could be a new DOM inside the current window, or as in the case of this demo it could be moving an already loaded iframe into another window that is on the same origin. (See update above).

Moving an iframe is interesting because it means that you don’t have to reboot the contents of the iframe, the instance is just moved. There are a couple of downsides:

  1. The URL remains on the current origin and not the iframe origin, although this might be something that the <portal> API could solve.
  2. If you are moving a custom element, or something that has it’s logic hosted on the opener - if you close the opener, execution will stop.

Disadvantages aside, I thought this DOM level IPC mechanism was very very interesting. Have a play with the demo page(src) and let me know if you have any interesting ideas for where this could be used.

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium

Likes

Hervé Tutuaku Uğur Aslan Paulo Elias Dmitrii Sorin Dion Almaer Saurabh Rajpal #WeAreWebinion KΞNNΞTH C. ⚡ Dr. Paul Mason 🇿🇦 🔞 Vikram is hiring! Dan Shappir Cícero Pablo Berrak 🥽 🌊 🏳️‍🌈 👩🏻‍💻 ☕️ Lars Knudsen 🇩🇰 Sarmad Saleem Eduardo ✈ AMPcs2019 Jonas 𝓞 𝓗 𝓒𝓪𝓶𝓼𝓱𝓪𝓯𝓽 Steven with a 3 Matt Wilcox Stefan Fejes Denis TRUFFAUT ⭐️ Adam Pearse Dion Almaer Arthur Stolyar Vikram is hiring! Rob Dodson Jasdeep Lalli Harun Hasdal Minko Gechev Rowan Merewood Pascal C

Reposts

Mustafa Kurtuldu Steven with a 3

Comments and Replies

Jon Paul DaviesJon Paul Davies
Same error here dude. Just now.
Paul KinlanPaul Kinlan
Yep - this is a great find.
Jake ArchibaldJake Archibald
Ahh but it's since been removed? That kinda explains our confusion
Simon PietersSimon Pieters
There was special behavior implemented for "magic iframe" back then readable-email.org/list/whatwg/to…
Jeremy SelierJeremy Selier
The iframe reload is the thing that prevents us to share the YouTube (iframe) player between videos in photos.google.com (and without it, each video creates its iframe+player which effectively sucks) :(
Paul KinlanPaul Kinlan
:) I'm not sorry, I appreciate the learning.
Jake ArchibaldJake Archibald
sorry not sorry for accidentally ruining your post
Harald @ 🇩🇪BerlinHarald @ 🇩🇪Berlin
Is BFCache maybe also contributing to the effect you are seeing?
Jake ArchibaldJake Archibald
But on that note, @annevk, do you know why iframes reload when they're moved around the DOM? It'd be nice if there was a way to make that not-happen.
Paul KinlanPaul Kinlan
I'm not sure, I need to check - personally, I just think I was plain wrong. :)
Alesandro Ortiz 🇵🇷Alesandro Ortiz 🇵🇷
Trying to access but getting an error. See screenshot, in case you aren't aware.