4

I have a page that has a header and sidebar with a right content panel that is an Iframe.

In a page loaded into the right content panel, I am trying to have a clicked link update the Browser URL in the parent window to the URL of the new page that is loaded into the Iframe.

I do not want the actual parent window to reload the URL but simply to update the URL in the address bar.

Something like:

window.history.pushState('obj', 'newtitle', '/bookmarks/list/');

Is this possible from an Iframe?

Andrew Myers
  • 2,754
  • 5
  • 32
  • 40
JasonDavis
  • 48,204
  • 100
  • 318
  • 537
  • _"Is this possibble from an Iframe?"_ Have you tried this approach ? – guest271314 Feb 20 '16 at 03:37
  • @guest271314 I have tried it, it updated the URL of the Iframe, so my iframe loaded a new page and had the new URL but the parent URL never changed – JasonDavis Feb 20 '16 at 03:44
  • 1
    I think I will try using the ifram `onmessage` events to send new url to parent from iframe and in the parent page, call the `window.history.pushState` from parent and see if that will do the magick – JasonDavis Feb 20 '16 at 03:48
  • See http://stackoverflow.com/questions/33645685/how-to-clear-the-contents-of-an-iframe-from-another-iframe/ – guest271314 Feb 20 '16 at 03:54
  • @guest271314 that post is to change the content of a page. I need to update the URL without reloading the page. THe idea posted above worked though. Using post message my parent receive the URL and then send a pushState to update the parent URL – JasonDavis Feb 20 '16 at 04:07

2 Answers2

3

I was able to accomplish updating the parent windows URL in the address bar using history.pushState by sending the new URL to the parent from the child Iframe window using postMessage and on the parent window listening for this event.

WHen the parent receives the child iframes postMessage event, it updates the URL with pushSTate using the URL passed in that message.

Child Iframe

<script>
// Detect if this page is loaded inside an Iframe window
function inIframe() {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}

// Detect if the CTRL key is pressed to be used when CTRL+Clicking a link
$(document).keydown(function(event){
    if(event.which=="17")
        cntrlIsPressed = true;
});
$(document).keyup(function(){
    cntrlIsPressed = false;
});
var cntrlIsPressed = false;

// check if page is loaded inside an Iframe?
if(inIframe()){
    // is the CTRL key pressed?
    if(cntrlIsPressed){
        // CTRL key is pressed, so link will open in a new tab/window so no need to append the URL of the link
    }else{

        // click even on links that are clicked without the CTRL key pressed
        $('a').on('click', function() {

            // is this link local on the same domain as this page is?
            if( window.location.hostname === this.hostname ) {

                // new URL with ?sidebar=no appended to the URL of local links that are clicked on inside of an iframe
                var linkUrl = $(this).attr('href');
                var noSidebarUrl = $(this).attr('href')+'?sidebar=no';

                // send URL to parent window
                parent.window.postMessage('message-for-parent=' +linkUrl , '*');

                alert('load URL with no sidebar: '+noSidebarUrl+' and update URL in arent window to: '+linkUrl);

                // load Iframe with clicked on URL content
                //document.location.href = url;
                //return false;
            }
        });
    }
}
</script>

Parent window

<script>

// parent_on_message(e) will handle the reception of postMessages (a.k.a. cross-document messaging or XDM).
function parent_on_message(e) {
    // You really should check origin for security reasons
    // https://developer.mozilla.org/en-US/docs/DOM/window.postMessage#Security_concerns
    //if (e.origin.search(/^http[s]?:\/\/.*\.localhost/) != -1
    //    && !($.browser.msie && $.browser.version <= 7)) {
        var returned_pair = e.data.split('=');
        if (returned_pair.length != 2){
            return;
        }
        if (returned_pair[0] === 'message-for-parent') {
            alert(returned_pair[1]);
            window.history.pushState('obj', 'newtitle', returned_pair[1]);
        }else{
            console.log("Parent received invalid message");
        }

    //}
}

jQuery(document).ready(function($) {
    // Setup XDM listener (except for IE < 8)
    if (!($.browser.msie && $.browser.version <= 7)) {
        // Connect the parent_on_message(e) handler function to the receive postMessage event
        if (window.addEventListener){
            window.addEventListener("message", parent_on_message, false);
        }else{
            window.attachEvent("onmessage", parent_on_message);
        }
    }
});
</script>
JasonDavis
  • 48,204
  • 100
  • 318
  • 537
3

Another solution using Window.postMessage().

Iframe:

<a href="/test">/test</a>
<a href="/test2">/test2</a>

<script>
Array.from(document.querySelectorAll('a')).forEach(el => {
  el.addEventListener('click', event => {
    event.preventDefault();
    window.parent.postMessage(this.href, '*');
  });
});
</script>

Main page:

Current URL: <div id="current-url"></div>
<iframe src="iframe-url"></iframe>

<script>
const $currentUrl = document.querySelector('#current-url');
$currentUrl.textContent = location.href;

window.addEventListener('message', event => {
  history.pushState(null, null, event.data);
  $currentUrl.textContent = event.data;
});
</script>

See demo on JS Fiddle.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177