Dynamic Web Coding  |  DynamicDrive
X

Moving back and forth on a main page containing an iframe whose content changes

If we remove an iframe from the DOM (using javascript) after having clicked on several links in the iframed page, the browser's back button scrolls back through all the iframe's history, meaning the button has to be clicked several times before another main page actually reloads. During all this time, we don't see happening anything on our page since the iframe has been removed. The reason for this behavior is that browsers can't distinguish between entries added to the history by the main window and entries added by the iframe's window. (Firefox is an exception: it removes all entries added by the iframe when the iframe is removed from the DOM). This is a serious problem for which there doesn't seem to exist a javascript solution. And in the hypothetical case that such a solution would exist, it would not work for iframes loading pages from a foreign domain.

The problem would be less serious if we could observe changes on our page each time the browser's history buttons are clicked on. There's a way to make this happen. We must not remove the iframe from the DOM when we don't want it to show on our page, but we just should make it invisible (display: none) while making sure that it reappears when the history buttons are hit.

I created a script that does just that for us (to see it happen, click on a link at the top of the page then click on several links in the iframed page that will be displayed and close the iframe whenever you want while moving back and forth on the main page using the browser's history buttons). It references the following lines, which must be put in the body (I kept the styles inline for readability; you may edit the bold parts):
<div id="ifr_wrapper" style="position: absolute; left: 10%; top: 20%; right: 10%; bottom: 10%; border: 1px solid black; box-shadow: 8px 8px 8px silver; background: gray; z-index: 1000; overflow: auto; -webkit-overflow-scrolling: touch; border-radius: 5px">
<div style="height: 7px"> </div>
<div style="position: absolute; top: -20px; background: #eeeeee; width: 100%; direction: rtl; font-family: arial; font-size: 26px; border: 1px solid black; border-bottom: 0; box-shadow: 5px 25px 15px silver inset; margin-left: -1px; padding: 5px;" >
<div id="ifr_url" style="position: absolute; left: 10px; font-size: 15px; margin-top: 25px; font-weight: bold" onmouseover="this.style.color='gray'" onmouseout="this.style.color='black'" ></div>
<div style="margin-top: 15px; padding-top: 2px; margin-right: 20px; " >
<span style="cursor: pointer; padding: 5px" onclick="document.getElementById('ifr_wrapper').style.display='none'; history.replaceState(null, null, '#iframe_hidden') " onmouseover="this.style.color='gray'" onmouseout="this.style.color='black'" >X</span> </div> </div><br>
<iframe sandbox="allow-same-origin allow-popups" name="the_ifr" id="the_ifr" frameborder="0" style="position: absolute; top: 40px; width: 100%; height: calc(100% - 40px); border-top: 1px solid black; background: white" src="about:blank" onload="no_mobile(); top.document.getElementById('ifr_wrapper').style.display='block'; history.replaceState(null, null, '#iframe_visible'); if(frames.the_ifr.location == 'about:blank') {history.replaceState(null,null,'#page_loaded')}; setTimeout('show_iframe()',0)"></iframe>
</div>

The sandbox attribute in the iframe must have allow-same-origin allow-popups. We can add more values if we want to allow more possibilities for the iframe, but that will cause the iframed pages to load slowly.

In the head (not in the body!):
<script>
function show_iframe() {
if(top.location.hash == '#iframe_visible'){setTimeout("document.getElementById('ifr_wrapper').style.display = 'block'",0)};
if(top.location.hash == '#iframe_hidden'){setTimeout("document.getElementById('ifr_wrapper').style.display = 'none'",0)};
if(top.location.hash == '#page_loaded'){setTimeout("document.getElementById('ifr_wrapper').style.display = 'none'",0)};
}

function no_mobile()
{
var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if(!isMobile)
{top.document.getElementById('ifr_wrapper').style.overflow = 'hidden'}
}
top.document.getElementById('the_ifr').addEventListener('load', function (){no_mobile(), show_iframe()}, false);
window.addEventListener('hashchange', function (){no_mobile(), show_iframe()}, false);
window.addEventListener('click', function (){no_mobile(), show_iframe()}, false);

function load_iframe(which)
{
frames.the_ifr.location=which;
history.replaceState(null, null, '#iframe_visible');
document.getElementById('ifr_url').innerHTML="<span style='text-decoration: underline; cursor: pointer' onclick='window.open(\""+which+"\")'>"+which+"</span>";
}
</script>

This script (and the onload in the iframe) uses the hashchange event to specify the fragment identifier of the main page-URL and to determine when the hash portion has changed. The iframe will be invisible if the hash portion of the URL is page_loaded or iframe_hidden. It will be visible if the hash portion is iframe_visible.

The lines (in the body) for 'opening' the iframe should look like this:
<a href="javascript: void(0)" onclick="load_iframe('http://www.dyn-web.com'); return false">Dynamic Web Coding</a>
<a href="javascript: void(0)" onclick="load_iframe('http://www.dynamicdrive.com'); return false">DynamicDrive</a>
etc.

Known issues:

That's it. Enjoy.
Arie Molendijk, mesdomaies.nu.