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:
- The script may not work properly when used locally. But there's no problem on the Internet.
- When the Microsoft Edge browser is used, then leaving the page containing our iframe by loading a new page will destroy all the entries added to the history by the iframe's window. (I don't know the behavior of the Microsoft Internet Explorer browser). This is exactly the behavior of Firefox when the iframe is actually removed from the DOM. So this is not a real issue, since clicking on the browser's history buttons will not produce a situation in which 'nothing happens' when the buttons are hit.
- When a page is loaded in our iframe, its custom address bar will display a link leading to that page. Clicking on the link will open it in a new tab. The link will not be present anymore when we load a new page and then come back to the page containing our iframe. There's nothing we can do about it. Fortunately, it's not a real problem.
That's it. Enjoy.
Arie Molendijk, mesdomaies.nu.