Change an iframe Parent Without Reload

If you’re playing around with iframes, there’s a nasty bug that occurs when you attempt to move the iframe using methods such as appendChild() or insertBefore(). How nasty the bug is depends on how tolerant your code is of reloading behaviors. Any attempts to move the iframe around will cause reloads in older Safari & Firefox versions. The problem lies in how the browsers interpret these events, removing the node and then adding it to its new destination which triggers a reload. If you’re trying to move iframes around on the page with something like a drag and drop interface, this rears its ugly head. Strangely enough, IE doesn’t exhibit this behavior, which leaves the problem to just Webkit and Firefox.

Workarounds in CSS

A quick scan reveals only one consistent technique for moving iframes about on the page using CSS- positioning absolutely. An absolutely positioned iframe can be readjusted easily, and can track directly on top of a placeholder such as a div. This works pretty fantastically in all but the most complex of layouts where absolute positioning requires a relative parent in just the right place.

In layouts that make use of lots of relative positioning, it’s easy to see where this might run into snags. Styles on either the div or iframe could mess up the alignment a bit, and while it’s correctable it’s not very feasible for a large scale site where there’s going to be several developers building code.

Webkit & Gmail: Adopting Nodes

In July 2010, Google silently released an improvement to Gmail. It was most noticeable when using the gmail chat feature. You could open the chat window, “pop” it out of the main window, and then *close the parent window* and everything continued to work. The most fascinating part about this was the minimal load time in the popup. It was as if the entire Gmail environment were being ported over to the popup as part of the unload events. That’s exactly what’s happening. The iframe contained the code both the main page and popup needed. The initial webkit bug on reparenting iframes would result in the ideal fix.

Somewhat quietly, DOM Level 3 core had added an operation called adoptNode(). Adopting an HTML node would simply move the node and in the case of the iframe, would not trigger a reload of the content. Coming back to Gmail, the JavaScript needed for the popup window is loaded into an iframe. When the parent window is closed, adoptNode() is called, moving all of the necessary code into the popup window before itself closing.

And Firefox? Opera? Insert-other-browser?

Unfortunately, as of this writing, Firefox still has [an open ticket on the broken adoptNode behavior, with no fixes in sight. As a last ditch effort, we can fall back to importNode(), we will just take the performance hit and trigger an undesirable reload which can possibly erase state within the iframe if there is any.

This stuff hasn’t been validated against Opera. Since Opera is based on webkit, it shouldn’t be exhibiting this bug.

Falling Back

Using the below code, it’s possible to use adoptNode() when it’s available, and then fall back to the importNode() as a last resort.

This article is from the archives of felocity.com. The original unedited post can be found in the github archives. It has recieved a quick once-over to modernize the content where applicable, but may contain references and links to code that is dead, unloved, or may simply no longer apply to modern web development.

Show your support

Clapping shows how much you appreciated Jakob Heuser’s story.